dubbo系列之zookeeper连接

dubbo系列之zookeeper连接

简介

dubbo和zookeeper是啥关系呢?打个比方:dubbo就是动物园的动物,zookeeper就是动物园,我们把很多不同的dubbo(动物)放到zookeeper(动物园中)提供给我们游客进行观赏。搞清楚了dubbo和zookeeper的关系,接下来我们看一下dubbo源码中是如何使用zookeeper的吧。

源码构成

RegistryFactory类

1
final Registry registry = getRegistry(originInvoker);//创建ZK连接

在RegistryFactory类中的export()方法中的上面那段代码,就是用于ZK连接,debug进去看看前,先看看他内部的时序图。

创建ZK连接

registryFactory.getRegistry(registryUrl)–>ExtensionLoader.getExtensionLoader(RegistryFactory.class).getExtension(“zookeeper”) –>extension.getRegistry(arg0)

                  -->AbstractRegistryFactory.getRegistry//创建一个注册中心,存储在REGISTRIES

​ –>createRegistry(url)
​ –>new ZookeeperRegistry(url, zookeeperTransporter)
​ –>AbstractRegistry

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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.getHost() + ".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();//把C:\Users\WYJ/.dubbo/dubbo-registry-127.0.0.1.cache文件中内容加载为properties
notify(url.getBackupUrls());//不做任何事
}

dubbo-registry-127.0.0.1.cache

1
2
3
#Dubbo Registry Cache
#Mon Mar 12 17:29:55 CST 2018
com.alibaba.dubbo.demo.DemoService=empty\://10.1.86.250\:20880/com.alibaba.dubbo.demo.DemoService?anyhost\=true&application\=demo-provider&category\=configurators&check\=false&dubbo\=2.0.0&generic\=false&interface\=com.alibaba.dubbo.demo.DemoService&loadbalance\=roundrobin&methods\=sayHello&owner\=william&pid\=5156&side\=provider&timestamp\=1520846955339

问:dubbo中zookeeper做注册中心,如果注册中心集群都挂掉,那发布者和订阅者还能进行通信吗?

答案是可以的,因为我们看到zookeeper的信息会缓存到本地作为一个缓存文件,并转换为properties对象方便使用。

FailbackRegistry

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public FailbackRegistry(URL url) {
super(url);
int retryPeriod = url.getParameter(Constants.REGISTRY_RETRY_PERIOD_KEY, Constants.DEFAULT_REGISTRY_RETRY_PERIOD);
//简历线程池
this.retryFuture = retryExecutor.scheduleWithFixedDelay(new Runnable() {
public void run() {
// 检测并连接注册中心
try {
//若失败,则重连
retry();
} catch (Throwable t) { // 防御性容错
logger.error("Unexpected error occur at failed retry, cause: " + t.getMessage(), t);
}
}
}, retryPeriod, retryPeriod, TimeUnit.MILLISECONDS);
}

这里的做法是,建立线程池,定时的检测并连接注册中心,如果失败了就重连。其实也就是一个`定时任务执行器。

ZookeeperRegistry

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public ZookeeperRegistry(URL url, ZookeeperTransporter zookeeperTransporter) {
super(url);
if (url.isAnyHost()) {
throw new IllegalStateException("registry address == null");
}
String group = url.getParameter(Constants.GROUP_KEY, DEFAULT_ROOT);
if (!group.startsWith(Constants.PATH_SEPARATOR)) {
group = Constants.PATH_SEPARATOR + group;
}
this.root = group;
zkClient = zookeeperTransporter.connect(url);//连接ZK,debug进去看看
zkClient.addStateListener(new StateListener() {
public void stateChanged(int state) {
if (state == RECONNECTED) {
try {
recover();//连接失败 重连
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
}
});
}

ZookeeperTransporter

1
2
3
4
5
6
7
@SPI("zkclient")
public interface ZookeeperTransporter {

@Adaptive({Constants.CLIENT_KEY, Constants.TRANSPORTER_KEY})
ZookeeperClient connect(URL url);

}

看一下zookeeperTransporter的类图和继承体系图

zookeeperTransporter继承体系图

问:zookeeper的java客户端你使用过哪些?

从继承体系图我们就只有,zookeeper是支持ZkClientCurator两种java客户端(其实就类似jedis是redis的java客户端一样),同时@SPI("zkclient")这里就可以看出它默认是用ZkClient的。

这样,zkClient = zookeeperTransporter.connect(url);也就走完了。接着看,接着重试机制。

1
2
3
4
5
6
7
8
9
10
11
  zkClient.addStateListener(new StateListener() {
public void stateChanged(int state) {
if (state == RECONNECTED) {
try {
recover();//连接失败 重连
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
}
});

以上就是整个zk连接的过程了,也就是执行了 getRegistry(originInvoker);这句代码。