Dubbo源码解析 — 服务订阅以及通知
前言
由于服务的注册内容比较多,所以把上一部分单独做一篇,订阅和通知合在一起。
subscribe
承接上一篇,我们说完了服务的发布,现在看看服务的订阅。考虑了一下,我还是决定在这里贴一下Protocol接口的相关注释,方便大家理解:
1 | /** |
这里很容易就可以明白,export发布,refer引用,然后获取invoker,然后执行invoker的方法即可。
继续看一下RegistryProtocol的refer方法:
1 | public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException { |
继续看一下doRefer方法:
1 | private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) { |
服务消费方不仅会订阅相关的服务,也会注册自身供其他层使用(服务治理)。特别要注意的是订阅时,同时订阅了三个分类类型:providers,routers,configurators。
继续深挖dubbo中服务消费方订阅服务的细节,上面方法中最终把订阅细节委托给RegistryDirectory.subscribe方法,注意,这个方法接受的参数,此时的url已经把category设置为providers,routers,configurators:
1 | public void subscribe(URL url) { |
这里registry就是zookeeperRegistry,这在doRefer方法可以看到明确的注入。然后和注册服务时一样,订阅会先由FailbackRegistry完成失效重试的处理,最终会交给zookeeperRegistry.doSubscribe方法。zookeeperRegistry实例拥有ZookeeperClient类型引用,该类型对象封装了和zookeeper通信的逻辑(默认是使用zkclient客户端)。我们在doSubscribe中能看到对zkClient的使用,而在ZK中的路径模式,统一看上一篇关于服务注册的最后一张图。
订阅之后,下面我们讲解notify,我们在doSubscribe中可以看到NotifyListener的调用。
Notify
这里先解决一个小问题,之前在其他博客上也看到过,dubbo中有一个ChildListener接口,并且我们在ZookeeperRegistry中会看到如下:
1 | private final ConcurrentMap<URL, ConcurrentMap<NotifyListener, ChildListener>> zkListeners = new ConcurrentHashMap<URL, ConcurrentMap<NotifyListener, ChildListener>>(); |
这里我还没用细究这个ChildListener的作用,借鉴了一篇博客里的讲解:
这个ChildListener接口用于把zkclient的事件(IZkChildListener)转换到registry事件(NotifyListener)。这么做的深意不是特别的理解,可能是因为我并没有太多zookeeper的使用经验导致的,这里的做法可以更好的把zkclient的api和dubbo真身的注册中心逻辑分离开,毕竟dubbo除了zkclient以外还可以选择curator。
我们主要看一下RegistryDirectory的notify方法:
1 | public synchronized void notify(List<URL> urls) { |
这里在每次消费方接受到注册中心的通知后,大概会做下面这些事儿:
- 更新服务提供方的配置规则
- 更新路由规则
- 重建invoker实例
我们这里主要看dubbo如何“重建invoker实例”,也就是最后一行代码调用的方法refreshInvoker:
1 | private void refreshInvoker(List<URL> invokerUrls){ |
到这里我们已经完成了服务通知的业务逻辑,有兴趣的童鞋可以深究一下toInvokers方法,它又会走一遍url -> invoker的逻辑(服务引用)。