dubbo-服务暴露过程之服务注册到注册中心

上面我们分析了服务暴露过程之网络通信的创建,接下来,我们跟踪下dubbo怎么将服务的URL信息注册到注册中心

  • dubbo封装了多个注册中心,屏蔽了不同注册中心之间的差异,提供了统一对外的API:
public interface RegistryService {

    /**
     * 注册数据,比如:提供者地址,消费者地址,路由规则,覆盖规则,等数据。
     */
    void register(URL url);

    /**
     * 取消注册.
     */
    void unregister(URL url);

    /**
     * 订阅符合条件的已注册数据,当有注册数据变更时自动推送.
     */
    void subscribe(URL url, NotifyListener listener);

    /**
     * 取消订阅.
     */
    void unsubscribe(URL url, NotifyListener listener);

    /**
     * 查询符合条件的已注册数据,与订阅的推模式相对应,这里为拉模式,只返回一次结果。
     */
    List<URL> lookup(URL url);
  • 从API接口中可以看到所有的注册中都提供了 注册(register),取消注册(unregister),订阅(subscribe),取消订阅(unsubscribe),查找(lookup)等几个操作方法。我们主要跟踪下注册方法的调用,从上面通信服务的创建,上次分析DUBBO在spring容器加载完毕后触发监听事件,调用Protocol.export对服务暴露,同时dubbo会先调用RegistryProtocol.export方法:
public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
        //此处默认是开启通信服务,默认调用的dubboProtocol,很关键invoker也不容易懂的地方
        //export
        final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker);

        //本地的通信服务开启成功后,将信息注册到注册中心,以供客户端发现
        //registry provider
        //获取注册中心服务实例信息,同样是通过扩展加载获取
        final Registry registry = getRegistry(originInvoker);
        //获取要注册服务的URL信息
        final URL registedProviderUrl = getRegistedProviderUrl(originInvoker);
        //将服务的URL信息注册到注册中心,以供客户端发现(dubbo://192.168.4.82:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&loadbalance=roundrobin&methods=sayHello&owner=william&pid=3996&scope=remote&side=provider&timestamp=1504851361655)
        //此处调用dubbo的注册组件服务进行服务对外注册
        registry.register(registedProviderUrl);
        //省略无关代码
        ........
        
    }

根据RegistryProtocol.export方法可以看到,此方法通过getRegistry(originInvoker)获取Registry(注册中心)实例,然后调用实例的register方法进行注册,那么继续根据获取Registry的代码:

    private Registry getRegistry(final Invoker<?> originInvoker) {
        URL registryUrl = originInvoker.getUrl();
        if (Constants.REGISTRY_PROTOCOL.equals(registryUrl.getProtocol())) {
            //此处重置了URL的 protocol参数,因为原先的protocol参数值应该为registry
            String protocol = registryUrl.getParameter(Constants.REGISTRY_KEY, Constants.DEFAULT_DIRECTORY);
            //注冊 protocol参数值默认为dubbo,在配置文件中可以动态设置 样例中设置为multicast
            registryUrl = registryUrl.setProtocol(protocol).removeParameter(Constants.REGISTRY_KEY);
        }
        //通过registryFactory自适应实例获取Registry 实际调用的是ExtensionLoader.getExtension(protocol);
        //zookeeper 注冊中心在我们实际项目中用的比较多下来我们主要分析下zookeeper注册中心的流程以及代码
        return registryFactory.getRegistry(registryUrl);
    }

这里的可以看到是通过registryFactory的getRegistry获取Registry实例的,那么registryFactory实例具体是哪个,这个与
Protocol的实例获取一致,是获取了一个动态生成的自适应类RegistryFactory$Adpative的实例,这个类实际调用ExtensionLoader.getExtension(protocol)获取registryFactory,如果是zookeeper的话,URL的protocol参数值应该是zookeeper,这样实际上是调用的ZookeeperRegistryFactory.getRegistry方法。

  • 动态创建RegistryFactory$Adpative类具体代码(此代码是动态生成的,dubbo的源码中无此类文件):
public class RegistryFactory$Adpative implements com.alibaba.dubbo.registry.RegistryFactory {
    public com.alibaba.dubbo.registry.Registry getRegistry(com.alibaba.dubbo.common.URL arg0) {
        if (arg0 == null) throw new IllegalArgumentException("url == null");
        com.alibaba.dubbo.common.URL url = arg0;
        String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
        if(extName == null) 
            throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.registry.RegistryFactory) name from url(" + url.toString() + ") use keys([protocol])");
        com.alibaba.dubbo.registry.RegistryFactory extension = (com.alibaba.dubbo.registry.RegistryFactory)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.registry.RegistryFactory.class).getExtension(extName);
        return extension.getRegistry(arg0);
    }
}

其它的注册中心获取的方式是一样,只是URL的protocol参数值不同,下来我们看下ZookeeperRegistryFactory.getRegistry的代码:

public Registry getRegistry(URL url) {
        url = url.setPath(RegistryService.class.getName())
                .addParameter(Constants.INTERFACE_KEY, RegistryService.class.getName())
                .removeParameters(Constants.EXPORT_KEY, Constants.REFER_KEY);
        String key = url.toServiceString();
        // 锁定注册中心获取过程,保证注册中心单一实例
        LOCK.lock();
        try {
            //首先在缓存中获取,当缓存中没有时创建,通过锁保证创建过程线程安全,创建单一的实例信息
            Registry registry = REGISTRIES.get(key);
            if (registry != null) {
                return registry;
            }
            registry = createRegistry(url);
            if (registry == null) {
                throw new IllegalStateException("Can not create registry " + url);
            }
            REGISTRIES.put(key, registry);
            return registry;
        } finally {
            // 释放锁
            LOCK.unlock();
        }
    }
public Registry createRegistry(URL url) {
        //Zookeeper 注册中心创建,直接new ZookeeperRegistry
        return new ZookeeperRegistry(url, zookeeperTransporter);
    }

上面这个两个方法getRegistry,createRegistry分别在AbstractRegistryFactory,ZookeeperRegistryFactory类中,AbstractRegistryFactory.getRegistry是所有注册中心通用部分,目的是使创建过程线程安全,保证注册中心单一实例,并将实例缓存到内存中,方便下次使用。ZookeeperRegistryFactory.createRegistry 创建ZookeeperRegistry注册中心

public ZookeeperRegistry(URL url, ZookeeperTransporter zookeeperTransporter) {
        super(url);
        if (url.isAnyHost()) {
            throw new IllegalStateException("registry address == null");
        }
        String group = url.getParameter(Constants.GROUP_KEY, DEFAULT_ROOT);
        if (!group.startsWith(Constants.PATH_SEPARATOR)) {
            group = Constants.PATH_SEPARATOR + group;
        }
        this.root = group;
        //创建zookeeper连接客户端
        zkClient = zookeeperTransporter.connect(url);
        //添加状态监听
        zkClient.addStateListener(new StateListener() {
            public void stateChanged(int state) {
                if (state == RECONNECTED) {
                    try {
                        recover();
                    } catch (Exception e) {
                        logger.error(e.getMessage(), e);
                    }
                }
            }
        });
    }

zookeeperTransporter 由ZookeeperRegistryFactory传入,此类是zookeeperTransporter的网络通信统一接口,有两个实现类ZkclientZookeeperTransporter,CuratorZookeeperTransporter,分别创建Curator与ZK类型的客户端。其中ZkclientZookeeperClient是对第三方org.I0Itec.zkclient.ZkClient的封装,CuratorZookeeperClient是对com.netflix.curator.framework.CuratorFramework的封装,两个都是操作zookeeper的客户端。从此可以看到ZookeeperRegistry实例中zkClient引用其实就是对org.I0Itec.zkclient.ZkClient或者对com.netflix.curator.framework.CuratorFramework的引用,ZookeeperRegistry下的注册,取消注册,订阅,取消订阅等方法都是通过zkClient实例来操作zookeeper服务实现的。至于zookeeper服务怎么注册path取消注册path,订阅,取消订阅,可以查看zookeeper的帮助文档学习。

  • 总结

通过上面的分析,可以看到注册首先会通过registryFactory获取一个定义的Registry,然后通过Registry操作具体的注册中心。本文中分析的zookeeper作为注册中心,它的流程如下:

RegistryFactory$Adpative.getRegistry()->ZookeeperRegistryFactory.getRegistry()->ZookeeperRegistryFactory.createRegistry()->new ZookeeperRegistry()---new ZkClient()
  • 备注

zookeeperTransporter 获取 ZkclientZookeeperTransporter,CuratorZookeeperTransporter的哪一个是由SPI机制实现的,后续SPI源码解析再细述。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容