• 6-Dubbo架构设计与底层原理-服务导出源码分析(下)


    服务导出过程

    Dubbo 服务导出过程始于 Spring 容器发布刷新事件,Dubbo 在接收到事件后,会立即执行服务导出逻辑。整个逻辑大致可分为三个部分,第一是前置工作,主要用于检查参数,组装 URL。第二是导出服务,包含导出服务到本地 (JVM),和导出服务到远程两个过程。第三是向注册中心注册服务,用于服务发现。

    Dubbo 支持两种服务导出方式,分别延迟导出和立即导出。延迟导出的入口是 ServiceBean 的 afterPropertiesSet 方法,立即导出的入口是 ServiceBean 的 onApplicationEvent 方法。分析服务延迟导出过程,因此不会分析 afterPropertiesSet 方法。

    从 onApplicationEvent 方法说起,该方法收到 Spring 容器的刷新事件后,会调用 export 方法执行服务导出操作。服务导出之前,要进行对一系列的配置进行检查,以及生成 URL。准备工作做完,随后开始导出服务。首先导出到本地,然后再导出到远程。导出到本地就是将服务导出到 JVM 中,此过程比较简单。
    导出到远程的过程则要复杂的多,以 dubbo 协议为例,DubboProtocol 类的 export 方法将会被调用。该方法主要用于创建 Exporter 和 ExchangeServer。

    ExchangeServer 本身并不具备通信能力,需要借助更底层的 Server 实现通信功能。因此,在创建 ExchangeServer 实例时,需要先创建 NettyServer 或者 MinaServer 实例,并将实例作为参数传给 ExchangeServer 实现类的构造方法。ExchangeServer 实例创建完成后,导出服务到远程的过程也就接近尾声了。服务导出结束后,服务消费者即可通过直联的方式消费服务。
    当然,一般不会使用直联的方式消费服务。所以,在服务导出结束后,紧接着要做的事情是向注册中心注册服务。此时,客户端即可从注册中心发现服务。

    服务注册

    服务注册操作对于 Dubbo 来说不是必需的,通过服务直连的方式就可以绕过注册中心。但通常不会这么做,直连方式不利于服务治理,仅推荐在测试环境测试服务时使用。以 Zookeeper 注册中心作为分析目标,从服务注册的入口方法开始分析, RegistryProtocol 的 export 方法上:

    public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
        
        // ${导出服务}    
        // 省略其他代码    
        boolean register = registeredProviderUrl.getParameter("register", true);
        if (register) {
            // 注册服务
            register(registryUrl, registeredProviderUrl);
            ProviderConsumerRegTable.getProviderWrapper(originInvoker).setReg(true);
        }
        
        final URL overrideSubscribeUrl = getSubscribedOverrideUrl(registeredProviderUrl);
        final OverrideListener overrideSubscribeListener = new 
            OverrideListener(overrideSubscribeUrl, originInvoker);
        overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);
        // 订阅 override 数据
        registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);
        // 省略部分代码
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    RegistryProtocol 的 export 方法包含了服务导出,注册,以及数据订阅等逻辑。

    public void register(URL registryUrl, URL registedProviderUrl) {
        // 获取 Registry
        Registry registry = registryFactory.getRegistry(registryUrl);
        // 注册服务
        registry.register(registedProviderUrl);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    register 方法包含两步操作,第一步是获取注册中心实例,第二步是向注册中心注册服务。

    创建注册中心

    以 Zookeeper 注册中心为例进行分析。先看一下 getRegistry 方法的源码,这个方法由 AbstractRegistryFactory 实现。如下:

    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 实例
            registry = createRegistry(url);
            if (registry == null) {
                throw new IllegalStateException("Can not create registry...");
            }        
            // 写入缓存
            REGISTRIES.put(key, registry);
            return registry;
        } finally {
            LOCK.unlock();
        }
    }
    protected abstract Registry createRegistry(URL url);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    getRegistry 方法先访问缓存,缓存未命中则调用 createRegistry 创建 Registry,然后写入缓存。这里的 createRegistry 是一个模板方法,由具体的子类实现。到 ZookeeperRegistryFactory 中探究一番。

    public class ZookeeperRegistryFactory extends AbstractRegistryFactory {
    
        // zookeeperTransporter 由 SPI 在运行时注入,类型为 ZookeeperTransporter$Adaptive
        private ZookeeperTransporter zookeeperTransporter;
    
        public void setZookeeperTransporter(ZookeeperTransporter zookeeperTransporter) {
            this.zookeeperTransporter = zookeeperTransporter;
        }
    
        @Override
        public Registry createRegistry(URL url) {
            // 创建 ZookeeperRegistry
            return new ZookeeperRegistry(url, zookeeperTransporter);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    ZookeeperRegistryFactory 的 createRegistry 方法仅包含一句代码

    public ZookeeperRegistry(URL url, ZookeeperTransporter zookeeperTransporter) {
        super(url);
        if (url.isAnyHost()) {
            throw new IllegalStateException("registry address == null");
        }
        
        // 获取组名,默认为 dubbo
        String group = url.getParameter(Constants.GROUP_KEY, DEFAULT_ROOT);
        if (!group.startsWith(Constants.PATH_SEPARATOR)) {
            // group = "/" + group
            group = Constants.PATH_SEPARATOR + group;
        }
        this.root = group;
        // 创建 Zookeeper 客户端,默认为 CuratorZookeeperTransporter
        zkClient = zookeeperTransporter.connect(url);
        // 添加状态监听器
        zkClient.addStateListener(new StateListener() {
            @Override
            public void stateChanged(int state) {
                if (state == RECONNECTED) {
                    try {
                        recover();
                    } catch (Exception e) {
                        logger.error(e.getMessage(), e);
                    }
                }
            }
        });
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    重点关注 ZookeeperTransporter 的 connect 方法调用,这个方法用于创建 Zookeeper 客户端。创建好 Zookeeper 客户端,意味着注册中心的创建过程就结束了。这里的 zookeeperTransporter 类型为自适应拓展类,因此 connect 方法会在被调用时决定加载什么类型的 ZookeeperTransporter 拓展,默认为 CuratorZookeeperTransporter。

    public ZookeeperClient connect(URL url) {
        // 创建 CuratorZookeeperClient
        return new CuratorZookeeperClient(url);
    }
    
    • 1
    • 2
    • 3
    • 4

    CuratorZookeeperClient

    public class CuratorZookeeperClient extends AbstractZookeeperClient<CuratorWatcher> {
    
        private final CuratorFramework client;
        
        public CuratorZookeeperClient(URL url) {
            super(url);
            try {
                // 创建 CuratorFramework 构造器
                CuratorFrameworkFactory.Builder builder = 
                    CuratorFrameworkFactory.builder()
                        .connectString(url.getBackupAddress())
                        .retryPolicy(new RetryNTimes(1, 1000))
                        .connectionTimeoutMs(5000);
                String authority = url.getAuthority();
                if (authority != null && authority.length() > 0) {
                    builder = builder.authorization("digest", authority.getBytes());
                }
                // 构建 CuratorFramework 实例
                client = builder.build();
                // 添加监听器
                client.getConnectionStateListenable().addListener(new 
                    ConnectionStateListener() {
                    @Override
                    public void stateChanged(CuratorFramework client, ConnectionState 
                    state) {
                        if (state == ConnectionState.LOST) {
                            CuratorZookeeperClient.this.stateChanged
                                (StateListener.DISCONNECTED);
                        } else if (state == ConnectionState.CONNECTED) {
                            CuratorZookeeperClient.this.
                                stateChanged(StateListener.CONNECTED);
                        } else if (state == ConnectionState.RECONNECTED) {
                            CuratorZookeeperClient.this.
                                stateChanged(StateListener.RECONNECTED);
                        }
                    }
                });
                
                // 启动客户端
                client.start();
            } catch (Exception e) {
                throw new IllegalStateException(e.getMessage(), e);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    CuratorZookeeperClient 构造方法主要用于创建和启动 CuratorFramework 实例。

  • 相关阅读:
    算法刷题-动态规划-1
    泛微OA E-Office V10 OfficeServer 任意文件上传漏洞复现
    【flutter上传图片】
    java基础之动态对象数组[19]
    JS 数据结构:集合
    unity 改变模型及其子物体的Shader和透明度
    单元测试的内容与步骤
    很详细的系列Shell基础— Shell简介
    四、机器学习基础
    Linux下如何打包库供别人使用
  • 原文地址:https://blog.csdn.net/xianghanscce/article/details/126065582