一、dubbo zk 数据结构#

在 ZooKeeper 基本概念分享一文讲道,ZK 内部是一种树形层次结构,节点存在多种类型。而 Dubbo 只会创建持久节点和临时节点。

若服务提供者服务接口为 com.service.FooService,将会在 ZK 中创建创建如下路径 /dubbo/com.service.FooService/providers/providerURL。

服务路径分为四层,根节点默认为 dubbo,可以在 dubbo-registry 设置 group 属性改变该值。

ps: 若无注册中心隔离需求,不要随便修改。

第二层节点为服务节点全名称,如 com.service.FooService。

第三层节点为服务目录,如 providers。另外还存在其他目录节点,分别为 consumers(消费者目录),configurators(配置目录),routers(路由目录)。下面服务订阅主要针对这一层节点。

第四个节点为具体服务节点,节点名为具体的 URL 字符串,如 dubbo://2.0.1.13:12345/com.dubbo.example.DemoService?xx=xx ,该节点默认为临时节点。

dubbo ZK 树形内部结构示例为:




dubbo 服务暴露流程 dubbo如何实现服务发现_zookeeper


ZK 内部服务具体示例如下:


dubbo 服务暴露流程 dubbo如何实现服务发现_zookeeper_02


二、RegistryFactory 实现#

Dubbo 可以在配置文件中指定使用注册中心,可以使用 dubbo.registry.protocol 指定具体注册中心类型,也可以设置 dubbo.registry.address 指定。注册中心相关实现将会使用 RegistryFactory 工厂类创建。

RegistryFactory 接口源码如下:

Copy@SPI("dubbo")public interface RegistryFactory { @Adaptive({"protocol"}) Registry getRegistry(URL url);}

RegistryFactory 接口方法使用 @Adaptive 注解,这里将会使用 Dubbo SPI 机制,自动生成代码的一些实现逻辑。这里将会根据 URL 中 protocol 属性,去调用最终实现子类。

RegistryFactory 实现子类如图所示:


dubbo 服务暴露流程 dubbo如何实现服务发现_dubbo k8s 服务发现_03


AbstractRegistryFactory 将会实现接口的 getRegistry 方法,主要完成加锁,并调用抽象模板方法 createRegistry 创建具体注册中心实现类,并将其缓存在内存中。

AbstractRegistryFactory#getRegistry 源码如下所示:

Copy public Registry getRegistry(URL url) { url = URLBuilder.from(url) .setPath(RegistryService.class.getName()) .addParameter(Constants.INTERFACE_KEY, RegistryService.class.getName()) .removeParameters(Constants.EXPORT_KEY, Constants.REFER_KEY) .build(); String key = url.toServiceStringWithoutResolving(); // 加锁,防止并发 LOCK.lock(); try { // 先从缓存中取 Registry registry = REGISTRIES.get(key); if (registry != null) { return registry; } //使用 Dubbo SPI 进制创建 registry = createRegistry(url); if (registry == null) { throw new IllegalStateException("Can not create registry " + url); } // 放入缓存 REGISTRIES.put(key, registry); return registry; } finally { // Release the lock LOCK.unlock(); } }

注册中心实例将会通过具体工厂类创建,这里我们看下 ZookeeperRegistryFactory 源码:

Copypublic class ZookeeperRegistryFactory extends AbstractRegistryFactory { private ZookeeperTransporter zookeeperTransporter; /** * 通过 Dubbo SPI 进制注入 * @param zookeeperTransporter */ public void setZookeeperTransporter(ZookeeperTransporter zookeeperTransporter) { this.zookeeperTransporter = zookeeperTransporter; } @Override public Registry createRegistry(URL url) { return new ZookeeperRegistry(url, zookeeperTransporter); }}

ps:Dubbo SPI 机制还具有 IOC 特性,这里的ZookeeperTransporter 注入可以参考:Dubbo 扩展点加载

三、zk 模块源码解析#

讲完注册中心实例创建过程,下面深入 ZookeeperRegistry 实现源码。

ZookeeperRegistry 继承 FailbackRegistry抽象类,所以其需要实现其父类抽象模板方法,下面主要了解 doRegister 与 doSubscribe源码 。

3.1 doRegister#

服务提供者需要将服务注册到注册中心,注册的目的是为了让消费者感知到服务的存在,从而发起远程调用,另一方面也让服务治理中心感知新的服务提供者上线。zk 模块服务注册代码比较简单,直接使用 zk 客户端在注册中心创建节点。

ZookeeperRegistry#doRegister 实现源码如下:

Copy public void doRegister(URL url) { try { zkClient.create(toUrlPath(url), url.getParameter(Constants.DYNAMIC_KEY, true)); } catch (Throwable e) { throw new RpcException("Failed to register " + url + " to zookeeper " + getUrl() +