一、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 树形内部结构示例为:
ZK 内部服务具体示例如下:
二、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 实现子类如图所示:
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() +