18. Dubbo服务暴露 - 服务监听
1. 开篇
本文介绍Dubbo服务的监听。
说到监听,那么作为一个服务提供者,到底要监听什么东西呢?
服务提供者主要监听的是动态配置,例如在dubbo-admin中对服务进行更改,服务提供者是需要感知到的。
动态配置包括服务的动态配置、应用的动态配置。以Zookeeper
为例,这些动态配置在Zookeeper
的存储路径为:
应用的动态配置:
/dubbo/config/dubbo/dubbo-demo-provier-application.configurators
服务的动态配置:
/dubbo/config/dubbo/org.apache.dubbo.DemoService::.configurators
应用的动态配置(老版本):
/dubbo/dubbo/org.apache.dubbo.DemoService/configurators
当这些动态配置数据发生改变时,对应的服务也会跟着改变。
2. 监听器
还记得前面说的监听器初始化入口在哪里吗?初始化入口是在RegistryProtocol#export
方法中。
final URL overrideSubscribeUrl = getSubscribedOverrideUrl(providerUrl);
final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker);
overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);
providerUrl = overrideUrlWithConfig(providerUrl, overrideSubscribeListener);
private URL overrideUrlWithConfig(URL providerUrl, OverrideListener listener) {
providerUrl = providerConfigurationListener.overrideUrl(providerUrl);
ServiceConfigurationListener serviceConfigurationListener = new ServiceConfigurationListener(providerUrl, listener);
serviceConfigurationListeners.put(providerUrl.getServiceKey(), serviceConfigurationListener);
return serviceConfigurationListener.overrideUrl(providerUrl);
}
OverrideListener
这个类实现了NotifyListener
接口,当有配置改动时,就会回调这个类的notify
方法。
providerConfigurationListener
是针对应用级别的监听,serviceConfigurationListeners
是针对服务级别的监听,因为服务可以有多个所以是个集合类型。
3. 应用监听器
public ProviderConfigurationListener() {
this.initWith(ApplicationModel.getApplication() + CONFIGURATORS_SUFFIX);
}
protected final void initWith(String key) {
ruleRepository.addListener(key, this);
String rawConfig = ruleRepository.getRule(key, DynamicConfiguration.DEFAULT_GROUP);
if (!StringUtils.isEmpty(rawConfig)) {
genConfiguratorsFromRawRule(rawConfig);
}
}
方法中的key
就是监听的路径,例如dubbo-demo-annotation-provider.configurators
,addListener(key, this)
就是对路径进行绑定监听。
rawConfig
就是从Zookeeper注册中心读取到的原始配置参数,例如:
configVersion: v2.7
configs:
- addresses:
- 0.0.0.0
enabled: false
parameters:
timeout: 6000
side: provider
enabled: true
key: dubbo-demo-annotation-provider
scope: application
拿到配置参数后,会把参数解析为一个configurators
对象。然后再调用providerConfigurationListener.overrideUrl
方法对传进来的URL进行重写,替换掉对应的参数,比如在dubbo-admin
中添加了动态参数设置timeout
为6s,调用overrideUrl
方法,就会把providerUrl
中原有的timeout
值给覆盖掉。
4. 服务监听器
同样的,服务监听器在初始化的时候,也会与一个路径进行绑定,先获取注册中心中配置的内容,并进行解析。调用ServiceConfigurationListener
类的overrideUrl
方法进行URL参数的重写。
可以看到,providerConfigurationListener
和ServiceConfigurationListener
都会对URL进行重写,但是ServiceConfigurationListener
是后处理的,所以可以得出结论:服务的配置优先级更高
。
5. 老版本的服务监听
registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);
老版本Dubbo服务的监听是这个方法处理的,处理方法的原理都是一样的,这里就不再赘述。 老版本是指dubbo 2.6.x
以及前的版本。
6. 服务监听示例
刚发看到了服务监控器实例化的过程,下面来操作一遍看监听是如何触发的。监听通知的方法在ServiceConfigurationListener
的notifyOverrides
方法中,我们来打一个断点,然后更改监听的内容。
将服务的超时时间更改为3001,点击保存。
我们看到ServiceConfigurationListener
就接收到了通知。
URL重写
接收到通知后,对URL的重写过程如下:
- 获取原始的服务URL(
originUrl
),未经过任何改变的URL。 - 获取当前服务的URL(
currentUrl
),当前处理的URL,可能之前被更改过。 - 对原始URL
originUrl
进行重写,这里重写会经过3次重写,老版本的重写、应用级别的重写、服务级别的重新,重写后得到一个newUrl
。 - 将
newUrl
与currentUrl
进行对别,如果不一致的话,这进行服务的重新导出。
7. 重新导出
该方法主要处理过程如下:
- 对新URL服务进行导出。
- 获取原来的注册URL、新的注册URL,进行对比。
- 如果不一致,将原来的URL对应服务执行卸载
unregister
。 - 将新的URL进行注册到注册中心,并更新缓存。
需要注意的是,重新导出与原来的导出逻辑基本一致,唯一不同的是由于第一次导出时ProtocolServer
对象被缓存了,所以在重新导出时,调用的是server.reset(url)
进行导出的,具体代码在DubboProtocol#openServer
。
8. 后记
本文主要介绍了服务对动态配置的逻辑处理以及服务的重新导出,