Dubbo源码|二十三、Dubbo服务引入——服务监听
前言
本文主要介绍Dubbo服务引入——服务监听部分,服务监听是监听服务的变动,比如提供者服务变动(新增、移除等)以及服务路由、动态配置的变动等。
正文
服务监听的代码位于RegistryProtocol#doCreateInvoker方法中。
protected <T> ClusterInvoker<T> doCreateInvoker(DynamicDirectory<T> directory, Cluster cluster, Registry registry, Class<T> type) {
directory.setRegistry(registry);
// ... 删除部分代码
directory.buildRouterChain(urlToRegistry);
directory.subscribe(toSubscribeUrl(urlToRegistry));
return (ClusterInvoker<T>) cluster.join(directory);
}
在该方法中有两处监听的设置,分别为buildRouterChain和subscribe。
buildRouterChain
从上一篇我们了解到在构造路由链时,会初始化4个类,分别为:MockRouterFactory、 TagRouterFactory、AppRouterFactory、ServiceRouterFactory,这几个工厂类中产生的实例对象中就有服务监听的处理逻辑。
buildRouterChain方法主要作用为构造路由链,路由链是动态服务目录中的一个属性,通过路由链可以过滤某些服务提供者,路由链会在引入服务时按路由条件进行过滤。
buildRouterChain方法里也会监听路由配置,因为新老版本路由配置存放的位置不一样,老版本是放在/dubbo/服务路径/routers里,而新版本则放在/dubbo/config/dubbo/服务路径里的。
例如,新版本监听路径:
/dubbo/config/dubbo/dubbo-demo-provider-application.tag-router和/dubbo/config/dubbo/org.apache.dubbo.demo.DemoService::.configurators
在构造路由链的过程中,会调用RouterChain的构造方法,通过factory工厂根据url来创建不同的路由。
private RouterChain(URL url) {
List<RouterFactory> extensionFactories = ExtensionLoader.getExtensionLoader(RouterFactory.class)
.getActivateExtension(url, ROUTER_KEY);
List<Router> routers = extensionFactories.stream()
.map(factory -> factory.getRouter(url))
.collect(Collectors.toList());
initWithRouters(routers);
}

RouterFactory也是通过SPI机制,来获取到不同的路由器工厂,从而创建不同的路由。
在创建路由的过程中,会调用父类——ListenableRouter的init方法,来配置监听并获取配置的规则,拿到这些规则后,调用process方法进行处理,将路由规则rule解析成对应的路由规则类。

subscribe
subscribe方法就是服务监听,为了兼容老版本,监听的目录有:
- 服务的消费应用目录:/dubbo/config/dubbo/dubbo-demo-consumer-application.configurators
- 服务的动态配置目录:/dubbo/config/dubbo/org.apache.dubbo.demo.DemoService:version:group.configurators
- 服务的提供者目录:/dubbo/org.apache.dubbo.demo.DemoService/providers
- 服务的动态配置目录(老版本):/dubbo/org.apache.dubbo.demo.DemoService/configurators
- 服务的路由器目录(老版本):/dubbo/org.apache.dubbo.demo.DemoService/routers
toSubscribeUrl方法为urlToRegistry添加了一个key——category,这个key对应的三个参数分别为:providers、configurators、routers,这三个参数代表着要监听的目录。
来看一下subscribe的具体实现:
- 将url记录到服务目录的consumerUrl属性中
- 监听consumer应用,即/dubbo/config/dubbo/dubbo-demo-consumer-application.configurators
- 监听服务的动态配置,即/dubbo/config/dubbo/org.apache.dubbo.demo.DemoService::.configurators

代码中registry.subscribe就是从注册中心中,将服务提供者的信息进行查询,并添加监听。因为我们使用的是Zookeeper作为注册中心,所以subscribe方法会经过FallbackRegistry类的subscribe方法,最终调到ZookeeperRegistry#doSubscribe方法。
// FallbackRegistry 类的subscribe方法
public void subscribe(URL url, NotifyListener listener) {
super.subscribe(url, listener);
removeFailedSubscribed(url, listener);
try {
// doSubscribe 是个抽象方法,由子类实现,这里是ZookeeperRegistry
doSubscribe(url, listener);
} catch (Exception e) {
addFailedSubscribed(url, listener);
}
}
doSubscribe
ZookeeperRegistry类的doSubscribe方法

doSubscribe做了如下几件事:
- 调用
toCategoriesPath方法,从url中解析category对应的值,对应的值分别为:providers、configurators、routers,也就是在前面添加的值。 - 根据
providers、configurators、routers这3个值以及服务名,生成要监听的服务路径 - 判断
zkListeners中是否存在url对应的监听器,如果没有的话则进行新建 - 调用
zkClient方法创建path节点 - 为
path节点添加监听 - 调用
notify方法,该方法会直接调用listener监听器的notify方法,也就是主动触发通知
notify

notify方法会对传入的参数进行分类,看这个写url时属于providers、configurators、routers中的哪一个,分类是根据url中参数的category属性值来判断的。
如果是configurators,则获取动态配置URL,生成configurators;
如果是routers,则生成Router,并添加到路由链中;
如果是providers,则解析出提供者的url。
最后调用refreshOverrideAndInvoker方法,进行参数刷新,因为有些参数,消费者和提供者都可以配置,但是消费者的优先级要比提供者的高,所以要覆盖提供者的参数,再将URL转换为Invoker。
总结
本文主要介绍Dubbo服务引入——服务监听部分,这部分源码内容比较多也比较绕,所以挑了几部分来介绍,由于本人水平有限未能面面俱到,还请较量。
转载自:https://juejin.cn/post/7220724477188440101