Dubbo 服务发现
总览
学习 Dubbo 的服务发现机制,可以从以下几方面入手:
- 注册中心的配置
- 服务的注册
- 客户端拉取服务列表
- 服务列表的本地缓存
- 服务提供者列表变更的监听机制
- 服务发现的接口设计
注册中心的配置
Dubbo 通过解析用户配置决定使用的注册中心。比如用户配置了 zookeeper://127.0.0.1:2181,Dubbo 在导出服务时解析出 name = zookeeper,便拿着这个名字到 ExtensionLoader 加载得到 ZookeeperServiceDiscoveryFactory,该工厂类生产出 ZookeeperServiceDiscovery,后续 ServiceDiscoveryRegistry 便可以通过调用 ZookeeperServiceDiscovery 的 doRegister 方法注册服务。
服务的注册
Spring 执行 refresh 方法后会触发 Dubbo 将服务注册到注册中心。
Spring 的 AbstractApplicationContext 执行 refresh 方法返回前,会发布 ContextRefreshedEvent, DubboBootstrapApplicationListener 接收到 ContextRefreshedEvent 后,会初始化并导出服务,最后将服务注册到注册中心。
/**
* An ApplicationListener to control Dubbo application.
*/
public class DubboDeployApplicationListener
implements ApplicationListener<ApplicationContextEvent>, ApplicationContextAware, Ordered {
@Override
public void onApplicationEvent(ApplicationContextEvent event) {
if (nullSafeEquals(applicationContext, event.getSource())) {
if (event instanceof ContextRefreshedEvent) {
onContextRefreshedEvent((ContextRefreshedEvent) event);
}
...
}
}
private void onContextRefreshedEvent(ContextRefreshedEvent event) {
ModuleDeployer deployer = moduleModel.getDeployer();
Assert.notNull(deployer, "Module deployer is null");
Object singletonMutex = LockUtils.getSingletonMutex(applicationContext);
// start module
Future future = null;
synchronized (singletonMutex) {
future = deployer.start();
}
...
}
}
ModuleDeployer 在 start 方法中调用了 registerServices 方法,完成了服务的注册。其调用链如下:
ModuleDeployer.registerServices -> ModuleDeployer.registerServiceInternal
-> ServiceConfigBase.register -> Exporter.register
其中,ModuleDeployer.registerServiceInternal 遍历 configManager 中的每一个服务,逐个对它们进行注册。
Exporter.register 实际调用的类视注册中心的不同而不同,以 ZooKeeper 为例,通过模版方法实现服务注册:
public abstract class FailbackRegistry extends AbstractRegistry {
@Override
public void register(URL url) {
if (!shouldRegister(url)) {
return;
}
super.register(url);
removeFailedRegistered(url);
removeFailedUnregistered(url);
try {
// Sending a registration request to the server side
doRegister(url);
} catch (Exception e) {
Throwable t = e;
// If the startup detection is opened, the Exception is thrown directly.
boolean check = getUrl().getParameter(Constants.CHECK_KEY, true)
&& url.getParameter(Constants.CHECK_KEY, true)
&& (url.getPort() != 0);
boolean skipFailback = t instanceof SkipFailbackWrapperException;
if (check || skipFailback) {
if (skipFailback) {
t = t.getCause();
}
throw new IllegalStateException(
"Failed to register " + url + " to registry " + getUrl().getAddress() + ", cause: "
+ t.getMessage(),
t);
} else {
logger.error(
INTERNAL_ERROR,
"unknown error in registry module",
"",
"Failed to register " + url + ", waiting for retry, cause: " + t.getMessage(),
t);
}
// Record a failed registration request to a failed list, retry regularly
addFailedRegistered(url);
}
}
}
doRegister 仅仅是在 Zookeeper 创建一个 ZNode(Zookeeper 文件系统中的节点):
@Override
public void doRegister(URL url) {
try {
checkDestroyed();
zkClient.create(toUrlPath(url), url.getParameter(DYNAMIC_KEY, true), true);
} catch (Throwable e) {
throw new RpcException(
"Failed to register " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);
}
}