RegistryDirectory,基于注册中心的服务发现,本文将重点探讨Dubbo是如何实现服务的自动注册与发现。从上篇文章,得知在消息消费者在创建服务调用器(Invoker)【消费者在初始时】时需要根据不同的协议,例如dubbo、registry(从注册中心获取服务提供者)来构建,其调用的方法为Protocol#refer,基于注册中心发现服务提供者的实现协议为RegistryProtocol。 
RegistryProtocol#refer —-> doRefer方法。 
RegistryProtocol#doRefer

private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {    // @1RegistryDirectory<T> directory = new RegistryDirectory<T>(type, url);   // @2directory.setRegistry(registry);directory.setProtocol(protocol);   // @3// all attributes of REFER_KEYMap<String, String> parameters = new HashMap<String, String>(directory.getUrl().getParameters());   // @4URL subscribeUrl = new URL(Constants.CONSUMER_PROTOCOL, parameters.remove(Constants.REGISTER_IP_KEY), 0, type.getName(), parameters);  // @5if (!Constants.ANY_VALUE.equals(url.getServiceInterface())&& url.getParameter(Constants.REGISTER_KEY, true)) {registry.register(subscribeUrl.addParameters(Constants.CATEGORY_KEY, Constants.CONSUMERS_CATEGORY,Constants.CHECK_KEY, String.valueOf(false)));}   // @6directory.subscribe(subscribeUrl.addParameter(Constants.CATEGORY_KEY,Constants.PROVIDERS_CATEGORY+ "," + Constants.CONFIGURATORS_CATEGORY+ "," + Constants.ROUTERS_CATEGORY));     // @7Invoker invoker = cluster.join(directory);    // @8ProviderConsumerRegTable.registerConsumer(invoker, url, subscribeUrl, directory);     // @9return invoker;}

代码@1:参数详解 
Cluster cluster:集群策略。 
Registry registry:注册中心实现类。 
Class type:引用服务名,dubbo:reference interface。 
URL url:注册中心URL。 
代码@2:构建RegistryDirectory对象,基于注册中心动态发现服务提供者(服务提供者新增或减少),本节重点会剖析该类的实现细节。 
代码@3:为RegistryDirectory设置注册中心、协议。 
代码@4:获取服务消费者的配置属性。 
代码@5:构建消费者URL,例如:

consumer://192.168.56.1/com.alibaba.dubbo.demo.DemoService?application=demo-consumer&check=false&dubbo=2.0.0&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=9892&qos.port=33333&side=consumer&timestamp=1528380277185

代码@6:向注册中心消息消费者:

consumer://192.168.56.1/com.alibaba.dubbo.demo.DemoService?application=demo-consumer&category=consumers&check=false&dubbo=2.0.0&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=9892&qos.port=33333&side=consumer&timestamp=1528380277185

相比第5步的URL,增加了category=consumers、check=false,其中category表示在注册中心的命令空间,这里代表消费端。该步骤的作用就是向注册中心为服务增加一个消息消费者,其生成的效果如下:【以zookeeper为例】。 
 
代码@7:为消息消费者添加category=providers,configurators,routers属性后,然后向注册中心订阅该URL,关注该服务下的providers,configurators,routers发生变化时通知RegistryDirectory,以便及时发现服务提供者、配置、路由规则的变化。

consumer://192.168.56.1/com.alibaba.dubbo.demo.DemoService?application=demo-consumer&category=providers,configurators,routers&check=false&dubbo=2.0.0&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=9892&qos.port=33333&side=consumer&timestamp=1528380277185

其订阅关系调用的入口为:RegistryDirectory#subscribe方法,是接下来需要重点分析的重点。 
代码@8:根据Directory,利用集群策略返回集群Invoker。 
代码@9:缓存服务消费者、服务提供者对应关系。 
从这里发现,服务的注册与发现与RegistryDirectory联系非常紧密,接下来让我们来详细分析RegistryDirectory的实现细节。 
1、RegistryDirectory类图 

1)private static final Cluster cluster = ExtensionLoader.getExtensionLoader(Cluster.class).getAdaptiveExtension(); 
集群策略,默认为failover。 
2)private static final RouterFactory routerFactory = ExtensionLoader.getExtensionLoader (RouterFactory.class).getAdaptiv 
eExtension()路由工厂,可以通过监控中心或治理中心配置。 
3)private static final ConfiguratorFactory configuratorFactory = ExtensionLoader.getExtensionLoader(ConfiguratorFactory 
.class).getAdaptiveExtension();配置实现工厂类。 
4)private final String serviceKey; 服务key,默认为服务接口名。com.alibaba.dubbo.registry.RegistryService,注册中心在 
Dubbo中也是使用服务暴露。 
5)private final Class< T > serviceType;服务提供者接口类,例如interface com.alibaba.dubbo.demo.DemoService 
6)private final Map< String, String> queryMap:服务消费者URL中的所有属性。 
7)private final URL directoryUrl;注册中心URL,只保留消息消费者URL查询属性,也就是queryMap。 
8)private final String[] serviceMethods:引用服务提供者方法数组。 
9)private final boolean multiGroup:是否引用多个服务组。 
10)private Protocol protocol:协议。 
11)private Registry registry:注册中心实现者。 
12)private volatile List< Configurator> configurators;配置信息。 
13)private volatile Map< String, Invoker< T>> urlInvokerMap; 服务URL对应的Invoker(服务提供者调用器)。 
14)private volatile Map< String, List< Invoker< T>>> methodInvokerMap; methodName : List< Invoker< T >>, 
dubbo:method 对应的Invoker缓存表。 
15)private volatile Set< URL > cachedInvokerUrls; 当前缓存的所有URL提供者URL。 
2、RegistryDirectory 构造方法详解

public RegistryDirectory(Class<T> serviceType, URL url) {    // @1super(url);if (serviceType == null)throw new IllegalArgumentException("service type is null.");if (url.getServiceKey() == null || url.getServiceKey().length() == 0)throw new IllegalArgumentException("registry serviceKey is null.");this.serviceType = serviceType;  this.serviceKey = url.getServiceKey();     // @2this.queryMap = StringUtils.parseQueryString(url.getParameterAndDecoded(Constants.REFER_KEY));  // @3this.overrideDirectoryUrl = this.directoryUrl = url.setPath(url.getServiceInterface()).clearParameters().addParameters(queryMap).removeParameter(Constants.MONITOR_KEY); //@4String group = directoryUrl.getParameter(Constants.GROUP_KEY, "");this.multiGroup = group != null && ("*".equals(group) || group.contains(","));String methods = queryMap.get(Constants.METHODS_KEY);this.serviceMethods = methods == null ? null : Constants.COMMA_SPLIT_PATTERN.split(methods);   // @5}

代码@1:参数描述,serviceType:消费者引用的服务< dubbo:reference interface=”” …/>;URL url:注册中心的URL,例如:

zookeeper://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?application=demo-consumer&dubbo=2.0.0&pid=5552&qos.port=33333&refer=application%3Ddemo-consumer%26check%3Dfalse%26dubbo%3D2.0.0%26interface%3Dcom.alibaba.dubbo.demo.DemoService%26methods%3DsayHello%26pid%3D5552%26qos.port%3D33333%26register.ip%3D192.168.56.1%26side%3Dconsumer%26timestamp%3D1528379076123&timestamp=1528379076179

代码@2:获取注册中心URL的serviceKey:com.alibaba.dubbo.registry.RegistryService。 
代码@3:获取注册中心URL消费提供者的所有配置参数:从url属性的refer。 
代码@4:初始化haulovverrideDirecotryUrl、directoryUrl:注册中心的URL,移除监控中心以及其他属性值,只保留消息消费者的配置属性。 
代码@5:获取服务消费者单独配置的方法名dubbo:method。 
3、RegistryDirectory#subscribe

public void subscribe(URL url) {setConsumerUrl(url);   // @1registry.subscribe(url, this); // @2
}

代码@1:设置RegistryDirectory的consumerUrl为消费者URL。 
代码@2:调用注册中心订阅消息消息消费者URL,首先看一下接口Registry#subscribe的接口声明: 
RegistryService:void subscribe(URL url, NotifyListener listener); 这里传入的NotifyListener为RegistryDirectory,其注册中心的subscribe方法暂时不深入去跟踪,不过根据上面URL上面的特点,应该能猜出如下实现关键点:

consumer://192.168.56.1/com.alibaba.dubbo.demo.DemoService?application=demo-consumer&category=providers,configurators,routers&check=false&dubbo=2.0.0&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=9892&qos.port=33333&side=consumer&timestamp=1528380277185

1)根据消息消费者URL,获取服务名。 
2)根据category=providers、configurators、routers,分别在该服务名下的providers目录、configurators目录、routers目录建立事件监听,监听该目录下节点的创建、更新、删除事件,然后一旦事件触发,将回调RegistryDirectory#void notify(List< URL> urls)。 
4、RegistryDirectory#notify 
首先该方法是在注册中心providers、configurators、routers目录下的节点发生变化后,通知RegistryDirectory,已便更新最新信息,实现”动态“发现机制。 
RegistryDirectory#notify

List<URL> invokerUrls = new ArrayList<URL>();
List<URL> routerUrls = new ArrayList<URL>();
List<URL> configuratorUrls = new ArrayList<URL>();
for (URL url : urls) {String protocol = url.getProtocol();    // @1 String category = url.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY);   // @2if (Constants.ROUTERS_CATEGORY.equals(category) || Constants.ROUTE_PROTOCOL.equals(protocol)) {   // @3routerUrls.add(url);} else if (Constants.CONFIGURATORS_CATEGORY.equals(category) || Constants.OVERRIDE_PROTOCOL.equals(protocol)) {   // @4configuratorUrls.add(url);} else if (Constants.PROVIDERS_CATEGORY.equals(category)) {    // @5invokerUrls.add(url);} else {logger.warn("Unsupported category " + category + " in notified url: " + url + " from registry " + getUrl().getAddress() + " to consumer " + NetUtils.getLocalHost());}
}

Step1:根据通知的URL的前缀,分别添加到:invokerUrls(提供者url)、routerUrls(路由信息)、configuratorUrls (配置url)。 
代码@1:从url中获取协议字段,例如condition://、route://、script://、override://等。 
代码@2:获取url的category,在注册中心的命令空间,例如:providers、configurators、routers。 
代码@3:如果category等于routers或协议等于route,则添加到routerUrls中。 
代码@4:如果category等于configurators或协议等于override,则添加到configuratorUrls中。 
代码@5:如果category等于providers,则表示服务提供者url,加入到invokerUrls中。 
RegistryDirectory#notify

// configurators
if (configuratorUrls != null && !configuratorUrls.isEmpty()) {this.configurators = toConfigurators(configuratorUrls);
}

Step2:将configuratorUrls转换为配置对象List< Configurator> configurators,该方法将在《源码分析Dubbo配置规则实现细节》一文中详细讲解。 
RegistryDirectory#notify

// routers
if (routerUrls != null && !routerUrls.isEmpty()) {List<Router> routers = toRouters(routerUrls);if (routers != null) { // null - do nothingsetRouters(routers);}
}

Step3:将routerUrls路由URL转换为Router对象,该部分内容将在《源码分析Dubbo路由机制实现细节》一文中详细分析。 
RegistryDirectory#notify

// providers
refreshInvoker(invokerUrls);

Step4:根据回调通知刷新服务提供者集合。 
5、RegistryDirectory#refreshInvoker 
RegistryDirectory#refreshInvoker

if (invokerUrls != null && invokerUrls.size() == 1 && invokerUrls.get(0) != null&& Constants.EMPTY_PROTOCOL.equals(invokerUrls.get(0).getProtocol())) {this.forbidden = true; // Forbid to accessthis.methodInvokerMap = null; // Set the method invoker map to nulldestroyAllInvokers(); // Close all invokers
} 

Step1:如果invokerUrls不为空并且长度为1,并且协议为empty,表示该服务的所有服务提供者都下线了。需要销毁当前所有的服务提供者Invoker。 
RegistryDirectory#refreshInvoker

this.forbidden = false; // Allow to access
Map<String, Invoker<T>> oldUrlInvokerMap = this.urlInvokerMap; // local reference
if (invokerUrls.isEmpty() && this.cachedInvokerUrls != null) {invokerUrls.addAll(this.cachedInvokerUrls);
} else {this.cachedInvokerUrls = new HashSet<URL>();this.cachedInvokerUrls.addAll(invokerUrls);//Cached invoker urls, convenient for comparison
}
if (invokerUrls.isEmpty()) {return;
}

Step2: 如果invokerUrls为空,并且已缓存的invokerUrls不为空,将缓存中的invoker url复制到invokerUrls中,这里可以说明如果providers目录未发送变化,invokerUrls则为空,表示使用上次缓存的服务提供者URL对应的invoker;如果invokerUrls不为空,则用iinvokerUrls中的值替换原缓存的invokerUrls,这里说明,如果providers发生变化,invokerUrls中会包含此时注册中心所有的服务提供者。如果invokerUrls为空,则无需处理,结束本次更新服务提供者Invoker操作。 
RegistryDirectory#refreshInvoker

Map<String, Invoker<T>> newUrlInvokerMap = toInvokers(invokerUrls);// Translate url list to Invoker map
Map<String, List<Invoker<T>>> newMethodInvokerMap = toMethodInvokers(newUrlInvokerMap); // Change method name to map Invoker Map

Step3:将invokerUrls转换为对应的Invoke,然后根据服务级的url:invoker映射关系创建method:List< Invoker>映射关系,将在下文相信分析。 
RegistryDirectory#refreshInvoker

this.methodInvokerMap = multiGroup ? toMergeMethodInvokerMap(newMethodInvokerMap) : newMethodInvokerMap;
this.urlInvokerMap = newUrlInvokerMap;
try {destroyUnusedInvokers(oldUrlInvokerMap, newUrlInvokerMap); // Close the unused Invoker
} catch (Exception e) {logger.warn("destroyUnusedInvokers error. ", e);
}

Step4:如果支持multiGroup机制,则合并methodInvoker,将在下文分析,然后根据toInvokers、toMethodInvokers刷新当前最新的服务提供者信息。 
6、RegistryDirectory#toInvokers 
RegistryDirectory#toInvokers

String queryProtocols = this.queryMap.get(Constants.PROTOCOL_KEY);
for (URL providerUrl : urls) {// ...
}

Step1:获取消息消费者URL中的协议类型,< dubbo:reference protocol=”” …/>属性值,然后遍历所有的Invoker Url(服务提供者URL)。 
RegistryDirectory#toInvokers

if (queryProtocols != null && queryProtocols.length() > 0) {boolean accept = false;String[] acceptProtocols = queryProtocols.split(",");for (String acceptProtocol : acceptProtocols) {if (providerUrl.getProtocol().equals(acceptProtocol)) {accept = true;break;}}if (!accept) {continue;}
}

Step2: 从这一步开始,代码都包裹在for(URL providerUrl : urls)中,一个一个处理提供者URL。如果dubbo:referecnce标签的protocol不为空,则需要对服务提供者URL进行过滤,匹配其协议与protocol属性相同的服务,如果不匹配,则跳过后续处理逻辑,接着处理下一个服务提供者URL。 
RegistryDirectory#toInvokers

if (Constants.EMPTY_PROTOCOL.equals(providerUrl.getProtocol())) {continue;
}

Step3:如果协议为empty,跳过,处理下一个服务提供者URL。 
RegistryDirectory#toInvokers

if (!ExtensionLoader.getExtensionLoader(Protocol.class).hasExtension(providerUrl.getProtocol())) {logger.error(new IllegalStateException("Unsupported protocol " + providerUrl.getProtocol() + " in notified url: " + providerUrl + " from registry " + getUrl().getAddress() + " to consumer " + NetUtils.getLocalHost()+ ", supported protocol: " + ExtensionLoader.getExtensionLoader(Protocol.class).getSupportedExtensions()));continue;
}

Step4:验证服务提供者协议,如果不支持,则跳过。 
RegistryDirectory#toInvokers

URL url = mergeUrl(providerUrl);

Step5:合并URL中的属性,其具体实现细节如下: 
1)消费端属性覆盖生产者端属性(配置属性消费者端优先生产者端属性),其具体实现方法:ClusterUtils.mergeUrl(providerUrl, queryMap),其中queryMap为消费端属性。 
a、首先移除只在服务提供者端生效的属性(线程池相关):threadname、default.threadname、threadpool、default.threadpool、corethreads、default.corethreads、threads、default.threads、queues、default.queues、alive、default.alive、transporter、default.transporter,服务提供者URL中的这些属性来源于dubbo:protocol、dubbo:provider。 
b、用消费端配置属性覆盖服务端属性。 
c、如下属性以服务端优先:dubbo(dubbo信息)、version(版本)、group(服务组)、methods(服务方法)、timestamp(时间戳)。 
d、合并服务端,消费端Filter,其配置属性(reference.filter),返回结果为:provider#reference.filter, 
consumer#reference.filter。 
e、合并服务端,消费端Listener,其配置属性(invoker.listener),返回结果为:provider#invoker.listener,consumer#invoker.listener。 
2)合并configuratorUrls 中的属性,我们现在应该知道,dubbo可以在监控中心或管理端(dubbo-admin)覆盖覆盖服务提供者的属性,其使用协议为override,该部分的实现逻辑见:《源码分析Dubbo配置规则机制(override协议)》 
3)为服务提供者URL增加check=false,默认只有在服务调用时才检查服务提供者是否可用。 
4)重新复制overrideDirectoryUrl,providerUrl在进过第一步参数合并后(包含override协议覆盖后的属性)赋值给overrideDirectoryUrl。

String key = url.toFullString(); // The parameter urls are sorted
if (keys.contains(key)) { // Repeated urlcontinue;
}
keys.add(key);

Step6:获取url所有属性构成的key,该key也是RegistryDirectory中Map

Map<String, Invoker<T>> localUrlInvokerMap = this.urlInvokerMap; // local reference
Invoker<T> invoker = localUrlInvokerMap == null ? null : localUrlInvokerMap.get(key);
if (invoker == null) { // Not in the cache, refer againtry {boolean enabled = true;if (url.hasParameter(Constants.DISABLED_KEY)) {enabled = !url.getParameter(Constants.DISABLED_KEY, false);} else {enabled = url.getParameter(Constants.ENABLED_KEY, true);}if (enabled) {invoker = new InvokerDelegate<T>(protocol.refer(serviceType, url), url, providerUrl);}} catch (Throwable t) {logger.error("Failed to refer invoker for interface:" + serviceType + ",url:(" + url + ")" + t.getMessage(), t);}if (invoker != null) { // Put new invoker in cachenewUrlInvokerMap.put(key, invoker);}
} else {newUrlInvokerMap.put(key, invoker);
}

Step7:如果localUrlInvokerMap中未包含invoker并且该provider状态为启用,则创建该URL对应的Invoker,并添加到newUrlInvokerMap中。toInvokers运行结束后,回到refreshInvoker方法中继续往下执行,根据 最新的服务提供者映射关系Map< String,Invoker>,构建Map< String,List< Invoker>>,其中键为methodName。然后更新RegistryDirectory的urlInvokerMap、methodInvokerMap属性,并销毁老的Invoker对象,完成一次路由发现过程。

上面整个过程完成了一次动态服务提供者发现流程,下面再分析一下RegistryDirectory的另外一个重要方法,doList,再重复一遍RegistryDirectory的作用,服务提供者目录服务,在集群Invoker的实现中,内部持有一个Direcotry对象,在进行服务调用之前,首先先从众多的Invoker中选择一个来执行,那众多的Invoker从哪来呢?其来源于集群Invoker中会调用Direcotry的public List< Invoker< T>> list(Invocation invocation),首先将调用AbstractDirectory#list方法,然后再内部调用doList方法,doList方法有其子类实现。

7、RegistryDirectory#doList(Invocation invocation) 方法详解 
RegistryDirectory#doList

if (forbidden) {// 1. No service provider 2. Service providers are disabledthrow new RpcException(RpcException.FORBIDDEN_EXCEPTION,"No provider available from registry " + getUrl().getAddress() + " for service " + getConsumerUrl().getServiceKey() + " on consumer " +  NetUtils.getLocalHost()+ " use dubbo version " + Version.getVersion() + ", please check status of providers(disabled, not registered or in blacklist).");
}

Step1:如果禁止访问(如果没有服务提供者,或服务提供者被禁用),则抛出没有提供者异常。 
RegistryDirectory#doList

Map<String, List<Invoker<T>>> localMethodInvokerMap = this.methodInvokerMap; // local reference
if (localMethodInvokerMap != null && localMethodInvokerMap.size() > 0) {String methodName = RpcUtils.getMethodName(invocation);Object[] args = RpcUtils.getArguments(invocation);if (args != null && args.length > 0 && args[0] != null&& (args[0] instanceof String || args[0].getClass().isEnum())) {invokers = localMethodInvokerMap.get(methodName + "." + args[0]); // The routing can be enumerated according to the first parameter}if (invokers == null) {invokers = localMethodInvokerMap.get(methodName);}if (invokers == null) {invokers = localMethodInvokerMap.get(Constants.ANY_VALUE);}if (invokers == null) {Iterator<List<Invoker<T>>> iterator = localMethodInvokerMap.values().iterator();if (iterator.hasNext()) {invokers = iterator.next();}}
}
return invokers == null ? new ArrayList<Invoker<T>>(0) : invokers;

Step2:根据方法名称,从Map< String,List< Invoker>>这个集合中找到合适的List< Invoker>,如果方法名未命中,则返回所有的Invoker,localMethodInvokerMap中方法名,主要是dubbo:service的子标签dubbo:method,最终返回invokers。

本文详细介绍了服务消费者基于注册中心的服务发现机制,其中对routers(路由)与configurators(override协议)并未详细展开,下节先重点分析configurators与routers(路由)实现细节。

总结一下服务注册与发现机制: 
基于注册 中心的事件通知(订阅与发布),一切支持事件订阅与发布的框架都可以作为Dubbo注册中心的选型。

1、服务提供者在暴露服务时,会向注册中心注册自己,具体就是在${service interface}/providers目录下添加 一个节点(临时),服务提供者需要与注册中心保持长连接,一旦连接断掉(重试连接)会话信息失效后,注册中心会认为该服务提供者不可用(提供者节点会被删除)。

2、消费者在启动时,首先也会向注册中心注册自己,具体在${interface interface}/consumers目录下创建一个节点。

3、消费者订阅${service interface}/ [ providers、configurators、routers ]三个目录,这些目录下的节点删除、新增事件都胡通知消费者,根据通知,重构服务调用器(Invoker)。

以上就是Dubbo服务注册与动态发现机制的原理与实现细节。

转载自https://blog.csdn.net/prestigeding/article/details/80727275

源码分析Dubbo服务注册与发现机制RegistryDirectory)相关推荐

  1. 源码分析Dubbo服务消费端启动流程

    通过前面文章详解,我们知道Dubbo服务消费者标签dubbo:reference最终会在Spring容器中创建一个对应的ReferenceBean实例,而ReferenceBean实现了Spring生 ...

  2. 源码分析Dubbo系列文章

       本系列文章主要针对Dubbo2.6.2(dubbox2.8.4)版本,从源码的角度分析Dubbo内部的实现细节,加深对Dubbo的各配置参数底层实现原理的理解,更好的指导Dubbo实践,其目录如 ...

  3. 源码分析Dubbo前置篇-寻找注册中心、服务提供者、服务消费者功能入口

    本节主要阐述如下两个问题:  1.Dubbo自定义标签实现.  2.dubbo通过Spring加载配置文件后,是如何触发注册中心.服务提供者.服务消费者按照Dubbo的设计执行相关的功能.  所谓的执 ...

  4. 源码分析Dubbo监控中心实现原理

       Dubbo监控的实现基本原理就是在服务调用时收集服务调用并发度.服务响应时间,然后以一定频率向监控中心汇报统计数据.    1.源码分析MonitorFilter过滤器 过滤器作用    监控过 ...

  5. dubbo源码分析系列(1)扩展机制的实现

    1 系列目录 dubbo源码分析系列(1)扩展机制的实现 dubbo源码分析系列(2)服务的发布 dubbo源码分析系列(3)服务的引用 dubbo源码分析系列(4)dubbo通信设计 2 SPI扩展 ...

  6. Dubbo源码解析-Dubbo服务消费者_Dubbo协议(一)

    前言: 在介绍完Dubbo 本地模式(Injvm协议)下的服务提供与消费后,上文我们又介绍了Dubbo远程模式(dubbo协议)下的服务暴露过程,本质上就是通过Netty将dubbo协议端口暴露出去, ...

  7. zookeeper源码分析之五服务端(集群leader)处理请求流程

    leader的实现类为LeaderZooKeeperServer,它间接继承自标准ZookeeperServer.它规定了请求到达leader时需要经历的路径: PrepRequestProcesso ...

  8. zookeeper源码分析之四服务端(单机)处理请求流程

    上文: zookeeper源码分析之一服务端启动过程 中,我们介绍了zookeeper服务器的启动过程,其中单机是ZookeeperServer启动,集群使用QuorumPeer启动,那么这次我们分析 ...

  9. Dubbo服务注册与发现的流程

    Dubbo 服务注册与发现的流程 1.流程说明: Provider(提供者)绑定指定端口并启动服务 指供者连接注册中心,并发本机IP.端口.应用信息和提供服务信息发送至注册中心存储 Consumer( ...

最新文章

  1. windows 下FFMPEG的编译方法 附2012-9-19发布的FFMPEG编译好的SDK下载
  2. Linux LXR 网站
  3. Discovery CentOS6.4 issue
  4. Django(part47)--文件上传
  5. Keil(MDK-ARM-STM32)系列教程(八)在线调试(Ⅰ)
  6. mysql5.7 json特性_【Mysql】Mysql5.7新特性之-json存储
  7. Android4.4 多媒体开发(五)----OpenMax简介
  8. 魔方机器人之下位机编程------下位机完整程序
  9. JSP教程第1讲笔记
  10. Python调试工具——ipdb
  11. word文档中英文行间距不一样怎么解决
  12. IdentityServer4 去掉验证
  13. 一套完整实用的IT规划方法论
  14. CF1680F Lenient Vertex Cover题解
  15. 双十一之后,留给证券区块链转型的时间不多了……
  16. 移动终端应用开发上机3组件通信与广播
  17. Mac录屏减少文件体积和格式转换
  18. 开关电源雷击浪涌整改_一种防雷击浪涌的开关电源电路设计
  19. 任务管理器-性能中各项的意义
  20. (操作系统原理·第三章)五个哲学家吃通心面 问题

热门文章

  1. Win 10 Revit 2019 安装过程,亲自踩的一遍坑,有你想要的细节
  2. beego2---入门
  3. mysql 用source 导入数据库报错
  4. Javascript自定义类
  5. GitHub中watch star fork三个按钮干什么用的?
  6. 普林斯顿公开课 算法1-5:算法理论
  7. Java关键字new和newInstance的区别
  8. android全系统动态二进制分析--CopperDroid
  9. spark基础之调度器运行机制简述
  10. 6 linux 制作raw命令_云计算网络知识学习-linux网络基础