09.消费者如何发现服务

到注册中心查找的堆栈

上一篇文章已经讲到provider会把自己服务监听的ip和端口等信息注册到注册中心。那么,consumer想要调用provider则需要到注册中心查找provider的信息。查找的调用栈是怎么样的呢?

先看下查找zk上的path的子节点的调用栈:

ZkclientZookeeperClient.addTargetChildListener(String, IZkChildListener) line: 88	
ZkclientZookeeperClient.addTargetChildListener(String, Object) line: 1	
ZkclientZookeeperClient(AbstractZookeeperClient<TargetChildListener>).addChildListener(String, ChildListener) line: 71	
ZookeeperRegistry.doSubscribe(URL, NotifyListener) line: 165	
ZookeeperRegistry(FailbackRegistry).subscribe(URL, NotifyListener) line: 189	
RegistryDirectory<T>.subscribe(URL) line: 133	
RegistryProtocol.doRefer(Cluster, Registry, Class<T>, URL) line: 271	
RegistryProtocol.refer(Class<T>, URL) line: 254	
ProtocolFilterWrapper.refer(Class<T>, URL) line: 60	
ProtocolListenerWrapper.refer(Class<T>, URL) line: 63	
Protocol$Adpative.refer(Class, URL) line: not available	
ReferenceBean<T>(ReferenceConfig<T>).createProxy(Map<String,String>) line: 392	
ReferenceBean<T>(ReferenceConfig<T>).init() line: 300	
ReferenceBean<T>(ReferenceConfig<T>).get() line: 138	
ReferenceBean<T>.getObject() line: 65	
DefaultListableBeanFactory(FactoryBeanRegistrySupport).doGetObjectFromFactoryBean(FactoryBean<?>, String) line: 168

查阅ZookeeperRegistry.doSubscribe(URL, NotifyListener) line: 165 附近的代码发现,在此通过给子节点添加监听器同时拿到子节点的办法查找到了provider端的信息,比如provider端的ip和端口等。

List<String> children = zkClient.addChildListener(path, zkListener);
if (children != null) {
    urls.addAll(toUrlsWithEmpty(url, path, children));
}

此处path是:/dubbo/com.code260.ss.dubbo.demov.facade.service.HelloService/providers

此处children是:

[dubbo://192.168.2.3:20880/com.code260.ss.dubbo.demov.facade.service.HelloService?anyhost=true&application=hello-world-app&connections=50&dubbo=2.0.0&interface=com.code260.ss.dubbo.demov.facade.service.HelloService&methods=sayHello&pid=56671&service.filter=tpslimiter&side=provider&timestamp=1590826560961]

为了看的方便,我对其做url解码,实际调试过程中看到的是上面这串字符串经过url编码后的值。

通过上述变量值,不难发现,consumer侧已经从zk上查到了provider的信息。

查到了provider端的ip与端口等信息后如何使用如何建立consumer端的client

对于dubbo protocol来讲,要完成服务调用则需要DubboInvoker。DubboInvoker的调用靠ExchangeClient完成请求。那么构建DubboInvoker的ExchangeClient是在其构造函数中,DubboInvoker构造函数的调用栈是:

DubboInvoker<T>.<init>(Class<T>, URL, ExchangeClient[], Set<Invoker<?>>) line: 62	
DubboProtocol.refer(Class<T>, URL) line: 303	
ProtocolFilterWrapper.refer(Class<T>, URL) line: 62	
ProtocolListenerWrapper.refer(Class<T>, URL) line: 65	
Protocol$Adpative.refer(Class, URL) line: not available	
RegistryDirectory<T>.toInvokers(List<URL>) line: 395	
RegistryDirectory<T>.refreshInvoker(List<URL>) line: 224	
RegistryDirectory<T>.notify(List<URL>) line: 195	
ZookeeperRegistry(AbstractRegistry).notify(URL, NotifyListener, List<URL>) line: 449	
ZookeeperRegistry(FailbackRegistry).doNotify(URL, NotifyListener, List<URL>) line: 273	
ZookeeperRegistry(FailbackRegistry).notify(URL, NotifyListener, List<URL>) line: 259	
ZookeeperRegistry.doSubscribe(URL, NotifyListener) line: 170

上面我截取了一部分,开始的地方是ZookeeperRegistry.doSubscribe,也正是到注册中心查找provider的ip和端口等信息的方法,也是其165行查找到结果的后续,170行做notify接上上面的堆栈。

164                    zkClient.create(path, false);
165                    List<String> children = zkClient.addChildListener(path, zkListener);
166                    if (children != null) {
167                    	urls.addAll(toUrlsWithEmpty(url, path, children));
168                    }
169                }
170               notify(url, listener, urls);

DubboProtocol.refer(Class, URL) line: 303 中会真正创建ExchangeClient。

把上面的堆栈大致画个流程图:

graph TD
A(RegistryProtocol.doRefer注册协议refer)-->B(RegistryDirectory.subscribe注册目录订阅)
B-->C(ZookeeperRegistry.doSubscribe向注册中心添加子路径的监听器同时查询子路径列表即provider侧的信息)
C-->D(ZookeeperRegistry.doSubscribe查找到provider信息后ZookeeperRegistry.notify)
D-->E(RegistryDirectory.notify/refreshInvoker/toInvokers)
E-->F(创建ExchangeClient实例-创建DubboInvoker实例等)

再精简一下

graph LR
A(添加监听器并查找)-->B(订阅)-->C(noitfy)-->D(创建client与invoker)

ReferenceConfig 把proxy建出来了就相当于发现了服务并完成消费端的”client“的创建。建proxy的过程中不仅包括查找到服务在哪里还包括增减监听器,包括创建Dubbo调用者等等…

创建client的代码:

com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol.getClients(URL)
com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol.getSharedClient(URL)
com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol.initClient(URL)
com.alibaba.dubbo.remoting.exchange.Exchangers.connect(URL, ExchangeHandler)

Exchangers这一层再往下再细节的后面讲。