【dubbo源码分析】3.dubbo消费端-Referenceconfig 初始化之远程注册中心方式初始化

11 篇文章 0 订阅
1 构建注册中心信息列表:
loadRegistries(false) 负责将注册中心集群配置地址
[<dubbo:registry address="10.0.28.54:2181;10.0.28.54:2182;10.0.28.54:2183" protocol="zookeeper" id="com.alibaba.dubbo.config.RegistryConfig" />]

解析成为单地址列表:
[
registry://10.0.28.54:2181/com.alibaba.dubbo.registry.RegistryService?application=demo-consumer&dubbo=2.0.0&pid=9996&registry=zookeeper×tamp=1519978079153, 	
registry://10.0.28.54:2182/com.alibaba.dubbo.registry.RegistryService?application=demo-consumer&dubbo=2.0.0&pid=9996&registry=zookeeper×tamp=1519978079153, 	
registry://10.0.28.54:2183/com.alibaba.dubbo.registry.RegistryService?application=demo-consumer&dubbo=2.0.0&pid=9996&registry=zookeeper×tamp=1519978079153
]
接着将 refer地址拼接至registry地址中,
[registry://10.0.28.54:2181/com.alibaba.dubbo.registry.RegistryService?application=demo-consumer&dubbo=2.0.0&pid=9996&refer=application%3Ddemo-consumer%26check%3Dfalse%26dubbo%3D2.0.0%26interface%3Dcom.alibaba.dubbo.demo.DemoService%26methods%3DsayHello%26pid%3D9996%26register.ip%3D10.0.28.54%26side%3Dconsumer%26timestamp%3D1519973599838&registry=zookeeper×tamp=1519978079153, 

registry://10.0.28.54:2182/com.alibaba.dubbo.registry.RegistryService?application=demo-consumer&dubbo=2.0.0&pid=9996&refer=application%3Ddemo-consumer%26check%3Dfalse%26dubbo%3D2.0.0%26interface%3Dcom.alibaba.dubbo.demo.DemoService%26methods%3DsayHello%26pid%3D9996%26register.ip%3D10.0.28.54%26side%3Dconsumer%26timestamp%3D1519973599838&registry=zookeeper×tamp=1519978079153, 

registry://10.0.28.54:2183/com.alibaba.dubbo.registry.RegistryService?application=demo-consumer&dubbo=2.0.0&pid=9996&refer=application%3Ddemo-consumer%26check%3Dfalse%26dubbo%3D2.0.0%26interface%3Dcom.alibaba.dubbo.demo.DemoService%26methods%3DsayHello%26pid%3D9996%26register.ip%3D10.0.28.54%26side%3Dconsumer%26timestamp%3D1519973599838&registry=zookeeper×tamp=1519978079153]
2 注册地址列表远程引用,若只有一个注册中地址,则不启动集群(这里只考虑集群):
refprotocol为Protocol的Adaptive类,根据Adaptive类生成规则可知,refprotocol.refer(class,url) 代码执行逻辑为:
public class Protocol$Adaptive implements com.alibaba.dubbo.rpc.Protocol {
    public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0, com.alibaba.dubbo.common.URL arg1) throws com.alibaba.dubbo.rpc.RpcException {
		com.alibaba.dubbo.common.URL url = arg1;
		String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
		com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
		return extension.refer(arg0, arg1);
	  }
}
由此可知 在Adaptive类获取Extension时返回的是 protocol="registry" 的扩展实例
com.alibaba.dubbo.rpc.Protocol extension = RegistryProtocol

在创建 RegistryProtocol 的instance时 【 getExtension(name) -> createExtension(name) -> class.newinstance() -> injectExtension(instance) 】
injectExtension 会根据实例所需的属性进行初始化设置 (根据set method来查找并注入的)

RegistryProtocol含有四个属性注入方法:
setCluster(Cluster cluster);
setProtocol(Protocol protocol);
setRegistryFactory(RegistryFactory registryFactory);
setProxyFactory(ProxyFactory proxyFactory);

最终返回的扩展点实例为 被包装后的 RegistryProtocol ,即 ProtocolFilterWrapper -> ProtocolListenerWrapper -> RegistryProtocol;调用链

2.1 最终进入到 RegistryProtocol.refer(class,url) 后

获取Registry详细过程如下:

RegistryProtocol.refer 主要流程:
2.1.1.获取注册中心Registry(见上图中的getRegistry流程);
2.1.2.根据上一步中Regisrty订阅消费者节点(见上图doRefer流程)

2.1.1)设置registryUrl 的protocol为 zookeeper后获取具体的Registry
流程执行过程:
registryFactory.getRegistry
-> ZookeeperRegistryFactory.getRegistry(url)
-> AbstractRegistryFactory.getRegistry(url)
-> ZookeeperRegistryFactory.createRegistry(url)
-> return new ZookeeperRegistry(url, zookeeperTransporter);
-> AbstractRegistry(url) -> FailbackRegistry(url) -> ZookeeperRegistry(url)
-> ZkClientZookeeperTransporter.connect(url) -> return new ZkclientZookeeperClient(url);
-> ZkclientWrapper(url.address) -> Zkclient(address) -> zkclient._connection.connect();
由 registryFactory Adaptive类 可知 registryFactory.getRegistry(url) 会根据 url.protocol获取具体的 RegistryFactory【 ZookeeperRegistryFactory】:

com.alibaba.dubbo.common.URL url = arg0;
String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() ); // url.protocol = zookeeper
com.alibaba.dubbo.registry.RegistryFactory extension = (com.alibaba.dubbo.registry.RegistryFactory)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.registry.RegistryFactory.class).getExtension(extName);
return extension.getRegistry(arg0);

接下来看看ZookeeperRegistryFactory.getRegistry(url):
首先统一从 AbstractRegistryFactory.getRegistry(url) 缓存中获取Registry,如果没有则创建心的Registry
AbstractRegistryFactory.getRegistry(url) -> ZookeeperRegistryFactory.createRegistry(url) -> return new ZookeeperRegistry(url, zookeeperTransporter);
当前 url = zookeeper://10.0.28.54:2181/com.alibaba.dubbo.registry.RegistryService?application=demo-consumer&dubbo=2.0.0&interface=com.alibaba.dubbo.registry.RegistryService&pid=9996&timestamp=1519978079153;
key = zookeeper://10.0.28.54:2181/com.alibaba.dubbo.registry.RegistryService,如果缓存中不存在,则重新创建。
返回含有 创建连接 zookeeperTransporter 属性的 新实例 ZookeeperRegistry 【 zookeeperTransporter 可选扩展类:ZkclientZookeeperTrasporter,CuratorZookeeperTrasporter,详情可查看 ZookeeperTrasporter之 Adaptive类】

================================================ZookeeperRegistry解析部分开始==========================================================
贯穿流程的 url : zookeeper://10.0.28.54:2181/com.alibaba.dubbo.registry.RegistryService?application=demo-consumer&dubbo=2.0.0&interface=com.alibaba.dubbo.registry.RegistryService&pid=9996&timestamp=1519978079153;

ZookeeperRegistry 继承关系如下:

因此初始化过程中执行链为:AbstractRegistry(url) -> FailbackRegistry(url) -> ZookeeperRegistry(url) 构造器;
1) >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> AbstractRegistry(url) 部分:
首先 > 启动文件保存定时器(默认为false),定时保存 C:\Users\0212149/.dubbo/dubbo-registry-demo-consumer-10.0.28.54:2181.cache 文件
本地文件保存路径结构为 : ${user.home}/.dubbo/dubbo-registry-${application-name}-${url.address}.cache;
其次 > 加载本地文件,获取服务注册列表

loadProperties()加载本地缓存文件获取服务列表:此时获取到信息:
{com.alibaba.dubbo.demo.DemoService=dubbo://10.0.28.54:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=9084&side=provider×tamp=1519637473382 
empty://10.0.28.54/com.alibaba.dubbo.demo.DemoService?application=demo-consumer&category=configurators&check=false&dubbo=2.0.0&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=7408&side=consumer×tamp=1519638606150 
empty://10.0.28.54/com.alibaba.dubbo.demo.DemoService?application=demo-consumer&category=routers&check=false&dubbo=2.0.0&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=7408&side=consumer×tamp=1519638606150}

notify(urls) -> 通知所有地址订阅者(包含自身地址)

2) >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> FailbackRegistry(url) 部分
FailbackRegistry(url) 初始化会启动重试线程,对注册失败,未注册成功的,订阅失败的地址列表定时进行重试,线程启动周期默认为5s

retry() 方法会对
failedRegistered ->重新注册(zookeeper 则重新尝试创建节点)
failedUnregistered ->删除节点(zookeeper 则删除节点)
failedSubscribed -> 创建listeners
failedUnsubscribed -> 删除listener
failedNotified -> listener重新执行notify
进行重试

====================================================== Transporter层 ====================================================================

3) >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>ZookeeperRegistry(url) 部分
获取zkclient : ZookeeperTransporter$Adaptive ->
String extName = url.getParameter("client", url.getParameter("transporter", "zkclient"));
ZookeeperTransporter extension = (com.alibaba.dubbo.remoting.zookeeper.ZookeeperTransporter)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.remoting.zookeeper.ZookeeperTransporter.class).getExtension(extName);
return extension.connect(arg0);

最终获取到 ZkClientZookeeperTransporter 执行 ZkClientZookeeperTransporter.connect(url)-> return new ZkclientZookeeperClient(url);



ZkclientZookeeperClient : 通过包装类 ZkclientWrapper 异步创建 ZkClient




相关zkclient连接启动日志:
至此 RegistryFactory.getRegistry(url) 流程结束,完成一个 Registry创建和启动
2.1.2 RegistryProtocol.doRefer()过程

RegistryProtocol.doRefer()主要做了几件事:
2.1.2.1.构建RegistryDirectory
2.1.2.2.使用Registry创建消费端节点
2.1.2.3.消费端订阅 [providers,configurations,routers] 节点,回调 RegistryDirectory,将服务列表List<URL> 转换成 invokers,并与缓存 invokers比较,删除未使用的invoker。
2.1.2.4.Cluster将 Directory 转换成Invoker,完成构建Invoker流程

1. RegistryProtocol.doRefer这里面进行的注册,将调用FailbackRegistry.registry进行真正的注册,实际跟zookeeper进行交互,调用的是ZookeeperRegistry的doRegistry方法。如果注册失败,会将url添加定定时任务中进行重试。
2.AbstractRegistry.notify真正接收到zookeeper的通知。在那儿将notify接口添加到zookeeper的改变事件的呢?在ZookeeperRegistry代码的doSubscribe方法中(与zookeeper进行交互的代码中),将订阅的接口发送给zookeeper,并且从 zookeeper取得可用的服务列表。
3.AbstractRegistry在收到通知后,将url进行存储,继续通知NotifyListener。NotifyListener是在哪儿放入Registry的呢?
在RegistryProtocol订阅服务的时候,会调用RegistryDirectory.subscribe方法。RegistryDirectory这类本身就实现了NotifyListener接口,在调用FailbackRegistry的subscribe方法的时候,把自己当成一个参数传递给了AbstractRegistry对象。所以AbstractRegistry在收到通知后,继续通知的是RegistryDirectory。
4. RegistryDirectory这个类维护着从本地方法到远程方法的映射关系,远程参数到本地方法的调用关系等。
在注册过程中的几个主要类
ZookeeperRegistry :负责与zookeeper进行交互
RegistryProtocol :从注册中心获取可用服务,或者将服务注册到zookeeper,然后提供服务或者提供调用代理。
RegistryDirectory :维护着所有可用的远程Invoker或者本地的Invoker。这个类实现了NotifyListner。
NotifyListener :负责RegistryDirectory和ZookeeperRegistry的通信。
FailbackRegistr y:继承自Registry,实现了失败重试机制。
问题:
1、dubbo是通过哪个类跟zookeeper进行交互的?
通过ZookeeperRegistry和Zookeeper进行交互,相关的类还有ZookeeperClient,ZkclientZookeeperClient和org.I0Itec.zkclient.ZkClient类。通过这些类的方法,实现服务的注册和订阅及信息的传递。
2、dubbo把订阅的信息存储到了哪个类?
主要是AbstractRegistry和RegistryDirectory这两个类。其中RegistryDirectory存储的可供客户端直接调用的Invoker,而AbstractRegistry这个类主要存储的是已经注册的服务接口,已经订阅的服务接口和已经收到通知的接口的URL,不能直接调用。

2.1.2.2 消费者地址节点创建(zookeeper中创建相关节点)

因为registryFactory.getRegistry(url) 返回了 ZookeeperRegistry,因此 执行 消费者注册时执行 ZookeeperRegistry.register(consumerUrl)
此时consumerUrl : consumer://10.0.28.54/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=9996&side=consumer&timestamp=1519973599838

最终执行 ZookeeperRegistry.doRegistry(url)
执行 ZkclientZookeeperClient.create(): 循环创建zookeeper节点,
此时 path = /dubbo/com.alibaba.dubbo.demo.DemoService/consumers/consumer%3A%2F%2F10.0.28.54%2Fcom.alibaba.dubbo.demo.DemoService%3Fapplication%3Ddemo-consumer%26category%3Dconsumers%26check%3Dfalse%26dubbo%3D2.0.0%26interface%3Dcom.alibaba.dubbo.demo.DemoService%26methods%3DsayHello%26pid%3D9996%26side%3Dconsumer%26timestamp%3D1519973599838

/dubbo/com.alibaba.dubbo.demo.DemoService/consumers/ 递归创建持久节点,
其余创建临时节点(随时可以删除)

最后进入到zklient 创建相关节点

至此,消费者节点创建完毕。

2.1.2.3 directory 订阅相关消费者节点

此时 consumerUrl = consumer://10.0.28.54/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=9996&side=consumer&timestamp=1519973599838
执行订阅相关节点: provider,configurations,routers

zookeeperRegistry.subscribe() 主要做两件事:·

接着调用 doSubscribe()
1) 添加当前订阅者到 subscribed 列表中 【AbstractRegistry 保存了所有已订阅列表,已注册服务列表,已接收通知列表】

2) 调用实现类方法 doSubscribe(final URL url, final NotifyListener listener) : 将订阅的接口发送给zookeeper,并且从zookeeper取得可用的服务列表。
调用将可服务列表通知notify
【notify(url,listener,urls)
url:consumer://10.0.28.54/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=9996&side=consumer&timestamp=1519973599838
listener: registryDirectory
urls:[dubbo://10.0.28.54:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=8292&side=provider&timestamp=1519710471473,
empty://10.0.28.54/com.alibaba.dubbo.demo.DemoService?application=demo-consumer&category=configurators&check=false&dubbo=2.0.0&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=9996&side=consumer&timestamp=1519973599838,
empty://10.0.28.54/com.alibaba.dubbo.demo.DemoService?application=demo-consumer&category=routers&check=false&dubbo=2.0.0&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=9996&side=consumer&timestamp=1519973599838]

notify 方法最终调用的是 AbstractRegistry.notify方法:

RegistryDirectory.notify(List<Url> urls) 在接收到新的url列表后首先会根据category进行分组 【category就是consumer订阅的监听目录: providers,configurators,routers】
并对分组后所有元素进行响应的操作,最后调用 refreshInvokers 对传送过来的 category 为provider的url 执行更新操作:删除没有被使用的invokers,更新现有invokers,
完成刷新工作( 详情请查看文章: DubboProtocol 服务消费 refer 过程
================================================ RegistryDirectory=====================

2.1.2.4 Cluster 将directory转换成Invoker

这里cluster为 Cluster$Adaptive类,默认为 FalioverCluster,又因为有包装类 MockClusterWrapper ;交互过程如下:
1.首先Cluster$Adaptive 会根据Directory中url获取 cluster属性(默认为 failover),根据cluster属性构建具体的Cluster;最终返回的Cluster为包装后的FalioverCluster;
即 new MockClusterWrapper(new FalioverCluster());

2.调用Cluster.join方法转换Directory成为Invoker:实际调用链为MockClusterWrapper.join() -> FalioverCluster.join()


最终构建成的invoker如下:



至此,Invoker创建完毕,依次返回到ReferenceConfig根据注册中心构建invoker处:
最后使用最后一个registryUrl作为StaticDirectory构造的url;并对registryUrl设置cluster=avalible;因此此处
cluster.join(new StaticDirectory(u,invokers))最终使用的是 AvalibleCluster


到这里,所有的invokers都创建完毕,存放在 AvalibleCluster.join 形成的AbstractClusterInvoker(StaticDirectory)里,见上图



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值