轻量级Rpc框架设计--motan源码解析五:Zookeeper注册服务

前言, motan框架向注册中心进行服务注册这部分源码实现, 大量使用了模板方法模式, 对于模板方法模式的学习, 可参考: 模板方法模式

一, Zookeeper注册服务

public <T> Exporter<T> export(Class<T> interfaceClass, T ref, List<URL> registryUrls) {

    	....
        .... 
        ....

        Provider<T> provider = new DefaultProvider<T>(ref, serviceUrl, interfaceClass);
        Exporter<T> exporter = protocol.export(provider, serviceUrl);
        // register service
        register(registryUrls, serviceUrl); // 向zk注册
        return exporter;
    }

经过前面几篇的分析, motan框架已经利用Netty完成了服务暴露, 接下来就是向zk注册自己暴露的服务, register(registryUrls, serviceUrl); 

1.1 register(registryUrls, serviceUrl); 

private void register(List<URL> registryUrls, URL serviceUrl) {
        for (URL url : registryUrls) {
            // 根据check参数的设置,register失败可能会抛异常,上层应该知晓
            RegistryFactory registryFactory = ExtensionLoader.getExtensionLoader(RegistryFactory.class).getExtension(url.getProtocol());
            Registry registry = registryFactory.getRegistry(url);
            registry.register(serviceUrl);
        }
    }

照例, motan框架还是使用Spi机制根据条件, 动态的加载向注册中心写数据的实现类 -- 这里当然就是加载操作zookeeper的ZookeeperRegistryFactory工厂类.

        多说一句, 对于框架而言, 这真是一种良好的设计方式, 很好的契合了开闭原则, 对已经实现了该功能模块的实现类, 修改是关闭的, 而对于想要新增一个针对该模块的实现类时, 提供一个入口, 就能很好的将新的实现方式加入, 也就是我们常说的对扩展开放.

1.2 Registry registry = registryFactory.getRegistry(url);

public Registry getRegistry(URL url) {
        String registryUri = getRegistryUri(url);
        try {
            lock.lock();
            Registry registry = registries.get(registryUri);
            if (registry != null) {
                return registry;
            }
            registry = createRegistry(url);
            if (registry == null) {
                throw new MotanFrameworkException("Create registry false for url:" + url, MotanErrorMsgConstant.FRAMEWORK_INIT_ERROR);
            }
            registries.put(registryUri, registry);
            return registry;
        } catch (Exception e) {
            throw new MotanFrameworkException("Create registry false for url:" + url, MotanErrorMsgConstant.FRAMEWORK_INIT_ERROR);
        } finally {
            lock.unlock();
        }
    }

getRegistry(url)方法, 首先从本地缓存中获取用于注册和发现的Registry实例对象, 没有的话, 就调用createRegistry()方法生成一个Registry对象, 并返回.

1.3 registry = createRegistry(url); // 创建一个新的Registry对象用于服务的注册和发现

protected Registry createRegistry(URL url) {
		return new ZookeeperRegistry(url);
	}

直接new创建一个新的ZookeeperRegistry对象, 调用其构造方法, 又初始化了一个ZkClient客户端连接对象, 用于操作zk, 如下:

public ZookeeperRegistry(URL url) {
        super(url);
        try {
            zkClient = new ZkClient(url.getParameter("address"));
        } catch (ZkException e) {
            LoggerUtil.error("[ZookeeperRegistry] fail to connect zookeeper, cause: " + e.getMessage());
        }
    }

调用其父类的构造方法, 每隔30s时间不断重试调用retry()方法, 去连接zk, 如下:

public FailbackRegistry(URL url) {
        super(url);
        long retryPeriod = url.getIntParameter(URLParamType.registryRetryPeriod.getName(), URLParamType.registryRetryPeriod.getIntValue());
        retryExecutor.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                try {
                    retry();
                } catch (Exception e) {
                    LoggerUtil.warn(String.format("[%s] False when retry in failback registry", registryClassName), e);
                }

            }
        }, retryPeriod, retryPeriod, TimeUnit.MILLISECONDS);
    }

经过上面的步骤后, 返回一个初始化后的ZookeeperRegistry实例对象, 调用其registry()方法向zk注册服务.

1.4 registry.register(serviceUrl);

public void register(URL url) {
        if (url == null) {
            LoggerUtil.warn("[{}] register with malformed param, url is null", registryClassName);
            return;
        }
        LoggerUtil.info("[{}] Url ({}) will register to Registry [{}]", registryClassName, url, registryUrl.getIdentity());
        doRegister(removeUnnecessaryParmas(url.createCopy()));
    }

调用doRegister()注册服务, 

public void doRegister(URL url) {
        failedRegistered.remove(url);
        failedUnregistered.remove(url);
        try {
            concreteRegister(url);
        } catch (Exception e) {
            if (isCheckingUrls(getUrl(), url)) {
                throw new MotanFrameworkException(String.format("[%s] false to registery %s to %s", registryClassName, url, getUrl()), e);
            }
            failedRegistered.add(url);
        }
    }

调用concreteRegister(url)注册服务, 

protected void concreteRegister(URL url) {
        String info = url.toFullStr();
        String serverTypePath = toServerTypePath(url);
        if (!zkClient.exists(serverTypePath)) {
            zkClient.createPersistent(serverTypePath, true);
        }
        String serverNodePath = toServerNodePath(url);
        if (zkClient.exists(serverNodePath)) {
            LoggerUtil.info(String.format("[ZookeeperRegistry] register: node exists, will be delete, path=%s", serverNodePath));
            zkClient.delete(serverNodePath);
        }
        zkClient.createEphemeral(serverNodePath, info);
        registeredUrls.add(url);
        LoggerUtil.info(String.format("[ZookeeperRegistry] register: path=%s, info=%s", serverNodePath, info));
    }

首先判断针对该服务在注册中心zk上是否已经存在该node节点, 不存在的话, 就使用zkClient操作zk生成一个临时node节点, 并向该node节点写入此次服务暴露的数据,

zkClient.createEphemeral(serverNodePath, info);

执行结果:

创建node节点,

serverNodePath = /motan/motan-demo-rpc/com.weibo.motan.demo.service.MotanDemoService/server/192.168.99.1:8001

向该节点写入服务暴露的参数数据,

info = motan://192.168.99.1:8001/com.weibo.motan.demo.service.MotanDemoService?maxContentLength=1048576&module=motan-demo-rpc&nodeType=service&accessLog=false&minWorkerThread=20&protocol=motan&isDefault=true&application=myMotanDemo&maxWorkerThread=800&shareChannel=true&refreshTimestamp=1544530358370&id=com.weibo.api.motan.config.springsupport.ServiceConfigBean&export=demoMotan:8001&requestTimeout=200&maxServerConnection=80000&group=motan-demo-rpc&

经过上面这些步骤后, motan就完成了向注册中心注册自己暴露的服务.

二, 总结

基于本篇(向zk注册中心注册自己的服务)和上一篇(Netty服务暴露)对motan框架的服务暴露源码解析, 即所谓的服务暴露, 其实归根到底, 核心就是干了两件事, 一是, 使用netty server绑定某个端口, 监听来自client客户端的调用请求, 接受请求, 处理请求并且返回执行结果. 二是, 向zk(不一定是zk作为注册中心)注册中心, 创建一个临时节点, 并且向该临时节点写入此次服务暴露的参数信息即可.

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值