一、服务端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信息的。