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×tamp=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
把上面的堆栈大致画个流程图:
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这一层再往下再细节的后面讲。
本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!