前言, 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作为注册中心)注册中心, 创建一个临时节点, 并且向该临时节点写入此次服务暴露的参数信息即可.