dubbo学习(五)服务发布-zookeeper的连接

2020-10-28 17:13:43 浏览数 (1)

一、服务端provider发布流程回顾

根据dubbo启动日志,provider的发布动作为以下几个步骤:

(1)暴露本地服务

Export dubbo service com.ywl.dubbo.TestApi to local registry, dubbo version: 2.0.0, current host: 127.0.0.1。

(2)暴露远程服务

Export dubbo service com.ywl.dubbo.TestApi to url dubbo://192.168.24.69:20880/com.ywl.dubbo.TestApi...后面省略。

(3)启动netty

Start NettyClient yuwenlei.local/192.168.24.69 connect to the server /192.168.1.100:20041, dubbo version: 2.0.0, current host: 192.168.24.69。

(4)打开zk

Opening socket connection to server dailyzk.webuy.ai/192.168.49.11:2181。

(5)注册provider服务到zk

Register dubbo service com.ywl.dubbo.TestApi url dubbo://192.168.24.69:20880/com.ywl.dubbo.TestApi? ...中间省略。

to registry registry://dailyzk.webuy.ai:7005/org.apache.dubbo.registry.RegistryService? ...后面省略。

(6)监听zk(订阅与通知)

Subscribe: provider://192.168.24.69:20880/com.ywl.dubbo.TestApi?...后面省略。

Notify urls for subscribe url provider://192.168.24.69:20880/com.ywl.dubbo.TestApi?...后面省略。

· 服务发布的目的

解析dubbo-provider.xml中的接口。将服务提供者向注册中心注册服务,以便服务消费者从注册中心查询并调用服务。

代码语言:javascript复制
<dubbo:service interface="com.ywl.dubbo.TestApi" ref="testApi" retries="0"
               cluster="failfast" timeout="3000"/>

二、zookeeper的连接

上篇中讲到了本地服务的暴露和远程服务的暴露(服务暴露和netty服务的暴露)。这篇主要分析的是远程服务暴露中的zookeeper连接的原理。

重新回到远程服务暴露的源码中,即org.apache.dubbo.registry.integration.RegistryProtocol#export

代码语言:javascript复制
public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
    //暴露服务
    final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker);
    URL registryUrl = getRegistryUrl(originInvoker);
    //获取注册中心信息 - 在这一步会进行zookeeper的连接
    final Registry registry = getRegistry(originInvoker);
    //...
代码语言:javascript复制
}

从上述源码中可以看到,暴露远程服务后,就需要获取注册中心信息,同时进行zookeeper的连接。

· 获取注册中心对象

代码语言:javascript复制
private Registry getRegistry(final Invoker<?> originInvoker) {
    URL registryUrl = getRegistryUrl(originInvoker);
    return registryFactory.getRegistry(registryUrl);
}

通过工厂模式创建注册对象,最终进入到AbstractRegistryFactory中进行注册中心对象的初始化。

代码语言:javascript复制
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);
    //key - zookeeper://zk的ip 端口/默认RegistryService路径名/group名/version号    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();
    }
}

上述源码比较简单,先根据url信息封装key-zookeeper前缀 zk的ip zk的端口 RegistryService路径名 配置的dubbo的group名 配置的dubbo的版本号组合为key。先根据key查询缓存,缓存存在则直接返回,缓存不存在则进行初始化,放入到缓存中。

· 创建注册中心

代码语言:javascript复制
public Registry createRegistry(URL url) {
    return new ZookeeperRegistry(url, zookeeperTransporter);
}//步骤一 加载本地配置到内存中
代码语言:javascript复制
public AbstractRegistry(URL url) {
    setUrl(url);
    syncSaveFile = url.getParameter(Constants.REGISTRY_FILESAVE_SYNC_KEY, false);
    String filename = url.getParameter(Constants.FILE_KEY, System.getProperty("user.home")   "/.dubbo/dubbo-registry-"   url.getParameter(Constants.APPLICATION_KEY)   "-"   url.getAddress()   ".cache");
    //获取配置信息本地缓存的文件    File file = null;
    if (ConfigUtils.isNotEmpty(filename)) {
        file = new File(filename);
        if (!file.exists() && file.getParentFile() != null && !file.getParentFile().exists()) {
            if (!file.getParentFile().mkdirs()) {
                throw new IllegalArgumentException("Invalid registry store file "   file   ", cause: Failed to create directory "   file.getParentFile()   "!");
            }
        }
    }    this.file = file;    //如果存在本地缓存文件则从文件中加载属性,文件中主要存放的是每个接口的注册中心的地址    loadProperties();
    //通知触发所有监听器    notify(url.getBackupUrls());
}
步骤二:检测注册中心,如果失败了进行重连public FailbackRegistry(URL url) {
    //...
    this.retryFuture = retryExecutor.scheduleWithFixedDelay(new Runnable() {
        @Override
        public void run() {
            try {                //建立线程池检测注册中心,如果失败了进行重连                retry();
            } catch (Throwable t) {
                logger.error("Unexpected error occur at failed retry, cause: "   t.getMessage(), t);
            }
        }
    }, retryPeriod, retryPeriod, TimeUnit.MILLISECONDS);
}步骤三:zookeeper连接
public ZookeeperRegistry(URL url, ZookeeperTransporter zookeeperTransporter) {
    //...    //初始化zookeeper,连接zookeeper    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);
                }
            }
        }
    });
}

创建注册中心的步骤也比较简单,最主要的目的为初始化zookeeper并连接zookeeper,并且存在订阅监听,如果连接断开则进行重连。

下一篇会分析下dubbo是如何订阅zookeeper信息的。

0 人点赞