23. Dubbo服务引入 - 服务监听
1. 前言
本文主要介绍Dubbo
服务引入——服务监听部分,服务监听是监听服务的变动,比如提供者服务变动(新增、移除等)以及服务路由、动态配置的变动等。
2. 正文
服务监听的代码位于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
。
2.1 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
解析成对应的路由规则类。
2.2 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);
}
}
2.3 doSubscribe
ZookeeperRegistry
类的doSubscribe
方法
doSubscribe
做了如下几件事:
- 调用
toCategoriesPath
方法,从url
中解析category
对应的值,对应的值分别为:providers
、configurators
、routers
,也就是在前面添加的值。 - 根据
providers
、configurators
、routers
这3个值以及服务名,生成要监听的服务路径 - 判断
zkListeners
中是否存在url
对应的监听器,如果没有的话则进行新建 - 调用
zkClient
方法创建path
节点 - 为
path
节点添加监听 - 调用
notify
方法,该方法会直接调用listener
监听器的notify
方法,也就是主动触发通知
2.4 notify
notify
方法会对传入的参数进行分类,看这个写url
时属于providers
、configurators
、routers
中的哪一个,分类是根据url
中参数的category
属性值来判断的。
如果是configurators
,则获取动态配置URL
,生成configurators
;
如果是routers
,则生成Router
,并添加到路由链中;
如果是providers
,则解析出提供者的url
。
最后调用refreshOverrideAndInvoker
方法,进行参数刷新,因为有些参数,消费者和提供者都可以配置,但是消费者的优先级要比提供者的高,所以要覆盖提供者的参数,再将URL
转换为Invoker
。
3. 总结
本文主要介绍Dubbo
服务引入——服务监听部分,这部分源码内容比较多也比较绕,所以挑了几部分来介绍,由于本人水平有限未能面面俱到,还请较量。