创建最新配置
时,只会把配置数据落到mysql,并不会马上把数据推到zk上,而需要监听该节点的disconf-client实例启动时,如果还没有创建zk节点,则由客户端创建节点并注册watch.所以启动流程中可能遇到的问题:
image.png
对于Web系统:
要实现统一读取,可以使用ThreadContext+AOP来实现。
ThreadContext的使用方式有以下几种:
解决方法一
:提供ThreadContext包,在每次请求一开始时都复制系统里的所有配置缓存(复制过程要与配置更新Sync互斥),从而保证每次会话的数据的一致性。
解决方法二
:提供ThreadContext包,每次请求都绑定一个版本号,如果读取时版本号不一致则报错,需要重新请求。
解决方法三
:方法二的加强版,添加一个注解定义,标注它是需要强一制性的,每次会话读取时只复制这些强一制性配置(复制过程要与配置更新Sync互斥)。
解决方法四
:提供ThreadContext包,系统内保存有多个配置缓存层,读取时统一读取某个版本的缓存。每当配置更新时,缓存层增加。
第一种方法,代价太大。第二种方法,严重增加用户负担,第三种还是需要用户关心这个事情。我们将采用第四种方法
。
对于非Web项目:
比较难解决非一致性读取的问题。因为它没有了会话这样一个概念。Apache的FileChangedReloadingStrategy Reload配置文件的方案也没有解决此问题。所以,我们打算放弃这方面的解决。但是,我们还是会提供一个简单却Ugly的解决方案:提供函数来标识用户读取配置的边界
。用户可以放弃使用这个方案,但是我们不保证不会发生“不一致读’问题。
服务启动前,zk连接不上:
服务启动前,zk连接上了:
注意
ZK一般需要以集群的形式提供出来。假设有N台ZK,
** * disconf-client的ZK异常处理
disconf-client可以完全保证: 如果在启动程序时保证ZK集群是可用的,那么,就可以保证在任何情况下,与ZK集群的自动连接。
下面按情况进行分析:
程序启动前,zk连接不上
这时disconf-client无法在ZK上注册信息。这是必须禁止发生的情况。也是disconf-client无法支持的情况。
一旦发生这种情况,请先恢复ZK集群,再启动你的程序。
程序启动前,zk连接上了:
如果在程序启动过程中,ZK是正常的,那么,disconf-client可以保证与ZK连接的自动性。
@Override
public void process(WatchedEvent event) {
//
// 结点更新时
//
if (event.getType() == EventType.NodeDataChanged) {
try {
LOGGER.info("============GOT UPDATE EVENT " + event.toString() + ": (" + monitorPath + "," + keyName
+ "," + disConfigTypeEnum.getModelName() + ")======================");
// 调用回调函数, 回调函数里会重新进行监控
callback();
} catch (Exception e) {
LOGGER.error("monitor node exception. " + monitorPath, e);
}
}
//
// 结点断开连接,这时不要进行处理,表示已经断开了和zk的连接
//
if (event.getState() == KeeperState.Disconnected) {
if (!debug) {
LOGGER.warn("============GOT Disconnected EVENT " + event.toString() + ": (" + monitorPath + ","
+ keyName + "," + disConfigTypeEnum.getModelName() + ")======================");
} else {
LOGGER.debug("============DEBUG MODE: GOT Disconnected EVENT " + event.toString() + ": (" +
monitorPath +
"," +
keyName +
"," + disConfigTypeEnum.getModelName() + ")======================");
}
}
//
// session expired,需要重新关注哦
//
if (event.getState() == KeeperState.Expired) {
if (!debug) {
LOGGER.error("============GOT Expired " + event.toString() + ": (" + monitorPath + "," + keyName
+ "," + disConfigTypeEnum.getModelName() + ")======================");
// 重新连接
ZookeeperMgr.getInstance().reconnect();
callback();
} else {
LOGGER.debug("============DEBUG MODE: GOT Expired " + event.toString() + ": (" + monitorPath + ","
+ "" + keyName + "," + disConfigTypeEnum.getModelName() + ")======================");
}
}
}
注意
disconf-client必须保证在程序在启动时,ZK集群的可用性。