dubbo源码-从客户端看Registry和Directory

Posted 小鱼堂

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了dubbo源码-从客户端看Registry和Directory相关的知识,希望对你有一定的参考价值。

简介

站在dubbo客户端的角度,核心目的是服务调用。在调用服务前,需要做2个重要的事情。

这2件事情涉及到重要的2个类RegistryDirectory,现分析他们的作用和原理。

Registry是和注册中心打交道的类,有多种实现,以zookeeper为例子进行分析。


结构

  • 对应客户端的应用来说,一个注册中心对应一个ZookeeperRegistry对象。ZookeeperRegistry中封装了zkClient和zk交互,用来创建节点和监听节点变更。也会缓存一份客户端需要的服务端信息到本地文件。

  • 一个要消费的服务(比如要调用远程服务UserService)对应一个RegistryDirectory对象,泛型T及是对应的服务接口。

    • RegistryDirectory中存放着所有可以调用的远程服务提供者机器列表invokers,会通过list方法按一定规则选择一起发起调用。


Registry的作用

  • 注册中心为Zookeeper,Registry的实例就为ZookeeperRegistry

  • ZookeeperRegistry的核心作用是和注册中心(zk)交互。包括创建节点,监听节点变更

  • ZookeeperRegistry中有和zkServer交互的zkClient,zkClient用的是Curator。用来实现对zk创建节点和添加监听

    • 因为ZookeeperRegistry是共用的一个对象,所以zklisteners的key是各个需要消费的服务,不同的服务由不同的监听来处理。value就是要消费的服务对应的RegistryDirectory对象,RegistryDirectory正好实现了listener方法。

Directory的作用

  • 他的registry属性就是ZookeeperRegistry的实例。

  • 一个消费者就有一个RegistryDirectory对象,但是他们的ZookeeperRegistry都是同一个对象。

  • 维护一个invokers列表,存放所有注册在zk上的服务提供者对应的invoker对象

  • 2个核心方法

    • list用来获取可用调用服务的invoker。因为不是全部注册的服务机器都可以用来调用。要受到路由规则和负载均衡的过滤

代码实现

入口

RegistryProtocol.refer

这个是实例化消费者bean,创建消费者bean对应的invoker需要调用的方法,包含了ZookeeperRegistry和RegistryDirectory的核心过程。


ZookeeperRegistry

创建

dubbo源码-从客户端看Registry和Directory

通过registryFactory创建Registry。

因为用zookeeper做配置中心举例,所以这里的registryFactory最终会调用ZookeeperRegistryFactory的createRegistry方法,创建一个ZookeeperRegistry。

注:registryFactory具体是哪个对象,按SPI的规则来判断

dubbo源码-从客户端看Registry和Directory

无论哪个服务的URL,在创建Registry的时候,URL的path都会替换成RegistryService.class.getName(),所以同一个注册中心,无论客户端调用哪个服务,这里的Key都是相同的

创建对象会先从缓存中按key获取(REGISTRIES是静态的map),没有才创建。由于同一个注册中心key是相同的,所以每次拿到的都是同一个ZookeeperRegistry对象

因为每一个注册的消费者bean都要走到这里和创建ZookeeperRegistry以便和zk做交互(注册和监听),所以要保证只创建一次,大家都用同一个ZookeeperRegistry,而加了锁。


创建zkClient

dubbo源码-从客户端看Registry和Directory

dubbo源码-从客户端看Registry和Directory

dubbo源码-从客户端看Registry和Directory

dubbo对原生的Curator又做了一层包装,为ZookeeperClient。在ZookeeperClient中才找到原生的Curator对zk的client连接

dubbo源码-从客户端看Registry和Directory

上面的builder创建client就是最底层通过对Curator对zk的操作。创建连接,添加监听并保证连接成功。

注意用法:blockUntilConnected。因为start是个异步操作,然后再用blockUntilConnected阻塞到连接成功或者超时,来获取连接的结果。


RegistryDirectory

活动最开始的refer方法,创建完ZookeeperRegistry后,就要开始创建RegistryDirectory了

dubbo源码-从客户端看Registry和Directory

创建好的ZookeeperRegistry会继续传到doRefer中来和new出来的RegistryDirectory交互。这里调用new方法,可以知道每个消费者bean都会对应一个自己的RegistryDirectory。directory.setRegistry(registry)则是所有的RegistryDirectory都会共用同一个ZookeeperRegistry。

完成创建后,看2个重要的方法register和subscribe


register

register会先调用FailbackRegistry的register,再调用子类ZookeeperRegistrydoRegister

dubbo源码-从客户端看Registry和Directory

dubbo源码-从客户端看Registry和Directory

dubbo源码-从客户端看Registry和Directory

核心作用是调用zkClient在注册中心创建一个consumer消费者的临时节点。

最底层调用的就是Curator的create().forPath()。

dubbo源码-从客户端看Registry和Directory

subscribe

subscribe其实调用的也是ZookeeperRegistry的subscribe。还是通过ZookeeperRegistry父类,最终调用到ZookeeperRegistry的doSubscribe

dubbo源码-从客户端看Registry和Directory

dubbo源码-从客户端看Registry和Directory

一般接口名不会为*,走else逻辑。

dubbo源码-从客户端看Registry和Directory

注册监听

  • toCategoriesPath会按类型将URL分解成3种url,分别是provider, configration, router

  • 然后for循环给他们子节点变更添加监听ChildListener。ChildListener的逻辑是调用ZookeeperRegistry的notify方法

  • 对他们都添加监听是在服务端ip发生变化(服务端机器上线或者下线),或者负载均衡和路由规则发生变化(通过管理控制台配置)的时候,都能及时触发ZookeeperRegistry.nofity的调用来更新RegistryDirectory的URL数据


通知

notify(url, listener, urls);

notify最终会调用到AbstractRegistry的notify方法

dubbo源码-从客户端看Registry和Directory

在循环中核心做2件事

  1. 调用RegistryDirectory的notify方法

  2. saveProperties将服务,路由,负载均衡配置保存到本地文件


RegistryDirectory的notify方法

RegistryDirectory也是listener, 他实现的监听就在这里被ZookeeperRegistry调用。

同时以后每次zk节点发生变动的时候,都会触发ZookeeperRegistry调用到RegistryDirectory的nofity方法。

dubbo源码-从客户端看Registry和Directory

可以看到,他对3种类型的URL都做了对应数据的处理。包括负载均衡,路由和服务提供者。

最后刷新了invoker列表

private void refreshInvoker(List<URL> invokerUrls)

dubbo源码-从客户端看Registry和Directory

通过URL,创建出具体的invokers, 并赋值缓存在RegistryDirectory的urlInvokerMap中。

private Map<String, Invoker<T>> toInvokers(List<URL> urls)

具体的invoker对象有会缓存来减少性能开销,

dubbo源码-从客户端看Registry和Directory

创建的时候,使用protocol.refer(serviceType, url),创建出底层的invoker对象,为以后发起远程调用做好对象的准备。

这个protocol.refer,最终是由dubboProtocol(通过SPI实现)的protocolBindingRefer创建出来的DubboInvoker实例(其实DubboInvoker外面也被包了好几层)

完成ZookeeperRegistry和RegistryDirectory的创建和赋值,并监听注册中心变更事件来更新自身的数据,就为以后发起RPC调用做好准备了。


流程

  1. 收到变更后会同步通知变更URL对应的RegistryDirectory,并更新本地缓存。

总结

  1. ZookeeperRegistry用来管理zk的注册和监听。一个注册中心全局只有一个ZookeeperRegistry对象

  2. RegistryDirectory同时也负责处理zk节点发生变化的事件。更新自己维护的url列表和invoker集合。

补充

本文只讲了ZookeeperRegistry和RegistryDirectory中的主线逻辑。包括注册和监听,维护url和invoker列表。ZookeeperRegistry和RegistryDirectory还有很多其他属性和方法,包括各种数据map的映射管理,时间轮询器,负载,路由策略的维护等等,就留到具体使用场景中再做介绍。


另外RegistryDirectory还有一个重要的list方法用来从invoker集合中按一定策略选取一个可用调用的对象,发起rpc调用,这个到客户端发起调用的时候再详细介绍了。


以上是关于dubbo源码-从客户端看Registry和Directory的主要内容,如果未能解决你的问题,请参考以下文章

dubbo源码—dubbo简介

Dubbo源代码实现三:注册中心Registry

[源码阅读] Dubbo注册中心模块实现原理

DUBBO源码学习

设计模式------从dubbo源码中学习观察者模式

源码解读Dubbo分层设计思想