了解了 Dubbo 的服务暴露、引用、触发的全过程后,在这我们总结下这些过程中涉及到的注册中心配置信息。通过了解这些配置信息,你可以多一个调试和理解 Dubbo 核心流程的视角。
ServcieConfig#export 来源
注册中心初始化和信息注册的关键代码如下:
// => org.apache.dubbo.registry.integration.RegistryProtocol#exportpublic <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {// ......// url to registryfinal Registry registry = getRegistry(registryUrl);final URL registeredProviderUrl = getUrlToRegistry(providerUrl, registryUrl);// ......}
获取 Registry 的流程:
其中
ServiceDiscoveryRegistry 不同于传统意义上的 registry(如 ZookeeperRegistry、NacosRegistry),它是 “面向服务”的。它不会直接和外部的注册中心直接交互,当调用 register(URL) 和 subscribe(URL, NotifyListener) 会存储服务暴露和引用的 urls 到 WritableMetadataService 里。
之后可以通过 WritableMetadataService.getExportedURLs() 等方法获取暴露的 urls,相对的,可以通过 WritableMetadataService.getSubscribedURLs() 获取订阅的 urls。
每个 ServiceDiscoveryRegistry 都拥有自己的 ServiceDiscovery 实例,其中 ServiceDiscovery 负责与外部注册中心的网络通讯,例如 ZookeeperServiceDiscovery 就是基于 Apache Curator X Discovery 封装的。
通过观察 ServiceDiscovery 接口,我们尝试分析 Dubbo 对注册中心的基础依赖要求:
@SPI("zookeeper")public interface ServiceDiscovery extends Prioritized {// 生命周期:支持初始化、销毁void initialize(URL registryURL) throws Exception;void destroy() throws Exception;boolean isDestroy();// 服务注册:CUD,少了个 Rvoid register(ServiceInstance serviceInstance) throws RuntimeException;void update(ServiceInstance serviceInstance) throws RuntimeException;void unregister(ServiceInstance serviceInstance) throws RuntimeException;// 服务发现:这就是那个 R 了// 获取服务列表Set<String> getServices();// 获取相关实例getInstances(...);// 事件支持:// 触发事件dispatchServiceInstancesChangedEvent(...);// 添加事件监听addServiceInstancesChangedListener(...);// 移除事件监听removeServiceInstancesChangedListener(...);// 监听服务实例变化ServiceInstancesChangedListener createListener(Set<String> serviceNames);}
ReferenceConfig#get 来源
以读和监听为主,在接口级服务发现时候会往编号 2 的 consumers 目录注册接口服务订阅者信息。
注册信息快查表
此处,我们以 zookeeper 为注册中心,通过 PrettyZoo 连接远端注册中心服务器,来看每一步都注册了什么。
// => org.apache.dubbo.registry.integration.RegistryProtocol#export
public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
// ......
// decide if we need to delay publish
boolean register = providerUrl.getParameter(REGISTER_KEY, true);
if (register) {
// 此处都是往 WritableMetadataService 写入,没有涉及到远端的注册中心
register(registry, registeredProviderUrl);
}
// ......
}
| 阶段 | 出场编号 | 目录 1 段 | 目录 2 段 | 目录 3 段 | 值 | 节点类型 | 作用 |
|---|---|---|---|---|---|---|---|
| 暴露 | 1 | /dubbo/config | /DUBBO_SERVICEDISCOVERY_MIGRATION | dubbo-demo-triple-api-provider.migration | 永久节点 | 迁移规则监听 | |
| 2 | /dubbo/{InterfaceName} 例如: /dubbo/org.apache.dubbo.demo.GreeterService |
/{CATEGORY} 例如:/providers 或 /consumers |
协议 URL tri%3A%2F%2F192.168.31.95%3A50051%2Forg.apache.dubbo.demo.GreeterService%3Fanyhost%3Dtrue%26application%3Ddubbo-demo-triple-api-provider%26deprecated%3Dfalse%26dubbo%3D2.0.2%26dynamic%3Dtrue%26generic%3Dfalse%26interface%3Dorg.apache.dubbo.demo.GreeterService%26metadata-type%3Dremote%26methods%3DsayHello%26pid%3D1965%26release%3D%26service-name-mapping%3Dtrue%26side%3Dprovider%26timestamp%3D1628859567247 |
IP 地址 似乎获取值有问题! |
临时节点 | 注册服务提供者相关信息 注册服务消费者相关信息 对于服务消费者来说,还会发起对 目录 2 段的监听,实现服务发现和动态路由 |
|
| 3 | /dubbo/{InterfaceName} 例如: /dubbo/org.apache.dubbo.demo.GreeterService |
/configurators | 永久节点 | 订阅服务 | |||
| 4 | /dubbo/metadata | /{InterfaceName}/{CATEGORY} 例如: /org.apache.dubbo.demo.GreeterService/provider |
应用名 例如: /dubbo-demo-triple-api-provider |
FullServiceDefinition 定义,使用 GSON 作序列化序列化对象是:服务接口定义 |
永久节点 | 元数据 注册提供者或消费者完整服务定义 |
|
| 5 | /dubbo/mapping | /org.apache.dubbo.demo.GreeterService | 应用名,例如: dubbo-demo-triple-api-provider |
永久节点 | 服务归属应用映射 用于用户在使用应用级服务发现,但未指定应用名时的兼容 |
||
| 6 | /dubbo/metadata | /org.apache.dubbo.metadata.MetadataService/{VERSION} 例如: /org.apache.dubbo.metadata.MetadataService/1.0.0 |
/dubbo-demo-triple-api-provider/provider/dubbo-demo-triple-api-provider | FullServiceDefinition 定义,使用 GSON 作序列化序列化对象是:MetadataService 接口 |
永久节点 | 注册 MetadataService 实例信息 | |
| 7 | /dubbo/metadata | /{APPLICATION_NAME} 例如: /dubbo-demo-triple-api-provider |
/{REVISION} 例如: /9eb93cf97837ad9b71d1a20eca044606 |
MetadataInfo 序列化对象,使用 GSON 作序列化 |
永久节点 | 注册 MetadataInfo 信息 | |
| 8 | /services | /{APPLICATION_NAME} 例如: /dubbo-demo-triple-api-provider |
/{ServiceInstance} 例如 /127.0.0.1:50051 |
存储服务实例信息,用于应用级服务发现 | 临时节点 | 用于监听应用变更通知 应用级服务发现重度依赖该配置信息 |
编号 1 来源于 MigrationRuleListener,
由 org.apache.dubbo.registry.integration.RegistryProtocol#notifyExport 触发:
// => org.apache.dubbo.registry.client.migration.MigrationRuleListener#MigrationRuleListener
public MigrationRuleListener() {
this.configuration = ApplicationModel.getEnvironment().getDynamicConfiguration().orElse(null);
// ......
// 尝试去远端获取 key 为 /dubbo/config/DUBBO_SERVICEDISCOVERY_MIGRATION/dubbo-demo-triple-api-provider.migration 的配置,该命令有类似 Linux mkdirp 的效果,如果上级目录不存在会尝试创建,但由于 key 值存在,就
String rawRule = configuration.getConfig(RULE_KEY, DUBBO_SERVICEDISCOVERY_MIGRATION);
}
编号 2 来源于 ZookeeperRegistry:
// => org.apache.dubbo.registry.zookeeper.ZookeeperRegistry#doRegister
public void doRegister(URL url) {
try {
// 具体放到 providers 还是 consumers 目录,依赖 URL 的 category 参数,默认是 providers
zkClient.create(toUrlPath(url), url.getParameter(DYNAMIC_KEY, true));
} catch (Throwable e) {
throw new RpcException("Failed to register " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);
}
}
// consumers 关键节点
// 1 => org.apache.dubbo.registry.integration.RegistryProtocol#getInvoker
// 2 => org.apache.dubbo.registry.integration.RegistryProtocol#doCreateInvoker
// providers 关键节点
// => org.apache.dubbo.registry.integration.RegistryProtocol#export
编号 3 来源于 ZookeeperRegistry:
// 上游触发是:org.apache.dubbo.registry.RegistryService#subscribe
// => org.apache.dubbo.registry.zookeeper.ZookeeperRegistry#doSubscribe
public void doSubscribe(final URL url, final NotifyListener listener) {
// ......
// 入参数的时候会尝试读取 key 值为 category 的 url 参数,确定生成哪些目录
// 例如 category=configurators
for (String path : toCategoriesPath(url)) {
ConcurrentMap<NotifyListener, ChildListener> listeners = zkListeners.computeIfAbsent(url, k -> new ConcurrentHashMap<>());
ChildListener zkListener = listeners.computeIfAbsent(listener, k -> new RegistryChildListenerImpl(url, path, k, latch));
if (zkListener instanceof RegistryChildListenerImpl) {
((RegistryChildListenerImpl) zkListener).setLatch(latch);
}
// 创建节点
zkClient.create(path, false);
List<String> children = zkClient.addChildListener(path, zkListener);
if (children != null) {
urls.addAll(toUrlsWithEmpty(url, path, children));
}
}
// ......
}
编号 4 来自于 ServiceConfig:
// => org.apache.dubbo.config.ServiceConfig#exportUrl
MetadataUtils.publishServiceDefinition(url);
// = > org.apache.dubbo.metadata.report.support.AbstractMetadataReport#storeProviderMetadata
public void storeProviderMetadata(MetadataIdentifier providerMetadataIdentifier, ServiceDefinition serviceDefinition) {
// 注意此方法可以是异步执行的,当初调试研究注册流程的时候,这边错过了很多次,浪费了一些时间。
if (syncReport) {
storeProviderMetadataTask(providerMetadataIdentifier, serviceDefinition);
} else {
reportCacheExecutor.execute(() -> storeProviderMetadataTask(providerMetadataIdentifier, serviceDefinition));
}
}
编号 5 来自于 ServiceConfig:
// => org.apache.dubbo.config.ServiceConfig#exported
protected void exported() {
exported = true;
List<URL> exportedURLs = this.getExportedUrls();
exportedURLs.forEach(url -> {
if (url.getParameters().containsKey(SERVICE_NAME_MAPPING_KEY)) {
ServiceNameMapping serviceNameMapping = ServiceNameMapping.getDefaultExtension();
// 映射服务名
serviceNameMapping.map(url);
}
});
onExported();
}
编号 6、7 来自于 DubboBootstrap:
// => org.apache.dubbo.config.bootstrap.DubboBootstrap#doStart
private void doStart() {
// ......
// If register consumer instance or has exported services
if (isRegisterConsumerInstance() || hasExportedServices()) {
// 2. export MetadataService
// 编号 6 来源
exportMetadataService();
// 3. Register the local ServiceInstance if required
// 编号 7 来源,同样是异步任务
// 后续传为 ServiceInstanceMetadataUtils.refreshMetadataAndInstance
registerServiceInstance();
}
// ......
}
编号 8 来自于 ZookeeperServiceDiscovery:
// => org.apache.dubbo.registry.zookeeper.ZookeeperServiceDiscovery#doRegister
// Provider 端触发,向注册中心注册服务实例信息,依赖 Zookeeper 的 ServiceDiscovery 拓展
public void doRegister(ServiceInstance serviceInstance) {
try {
serviceDiscovery.registerService(build(serviceInstance));
} catch (Exception e) {
throw new RpcException(REGISTRY_EXCEPTION, "Failed register instance " + serviceInstance.toString(), e);
}
}
// => org.apache.dubbo.registry.zookeeper.ZookeeperServiceDiscovery#registerServiceWatcher
// Consumer 端也会尝试创建目录,但不会往目录里面写服务实例
protected void registerServiceWatcher(String serviceName, ServiceInstancesChangedListener listener) {
String path = buildServicePath(serviceName);
try {
curatorFramework.create().creatingParentsIfNeeded().forPath(path);
}
// ......
}
