在《soul网关数据同步之zookeeper同步方式探究(1)——admin端同步》一文中,我们分析了admin端元数据同步到zk服务端的大致原理,本文继续分析bootstrap端与zk的元数据同步原理(zk版本为3.5.9),bootstrap的yml配置修改如下:
soul :
file:
enabled: true
corss:
enabled: true
dubbo :
parameter: multi
sync:
# websocket :
# urls: ws://localhost:9095/websocket
zookeeper:
url: localhost:2181
sessionTimeout: 5000
connectionTimeout: 2000
注:之前在启动bootstrap时,遇到了一些问题,记录下下面两篇文章中:
(1) soul网关学习之bootstrap启动报错(问题记录)
(2).soul网关学习之bootstrap启动报错-问题初步解决
分析过程
- 启动bootstrap,会看到如下一行日志:
INFO 14692 --- [ main] s.b.s.d.z.ZookeeperSyncDataConfiguration : you use zookeeper sync soul data.......
- ok,就从ZookeeperSyncDataConfiguration 类入手,该类内容如下:
public class ZookeeperSyncDataConfiguration {
/**
* Sync data service sync data service.
*
* @param zkClient the zk client
* @param pluginSubscriber the plugin subscriber
* @param metaSubscribers the meta subscribers
* @param authSubscribers the auth subscribers
* @return the sync data service
*/
@Bean
public SyncDataService syncDataService(final ObjectProvider<ZkClient> zkClient, final ObjectProvider<PluginDataSubscriber> pluginSubscriber,
final ObjectProvider<List<MetaDataSubscriber>> metaSubscribers, final ObjectProvider<List<AuthDataSubscriber>> authSubscribers) {
log.info("you use zookeeper sync soul data.......");
return new ZookeeperSyncDataService(zkClient.getIfAvailable(), pluginSubscriber.getIfAvailable(),
metaSubscribers.getIfAvailable(Collections::emptyList), authSubscribers.getIfAvailable(Collections::emptyList));
}
/**
* register zkClient in spring ioc.
*
* @param zookeeperConfig the zookeeper configuration
* @return ZkClient {@linkplain ZkClient}
*/
@Bean
public ZkClient zkClient(final ZookeeperConfig zookeeperConfig) {
return new ZkClient(zookeeperConfig.getUrl(), zookeeperConfig.getSessionTimeout(), zookeeperConfig.getConnectionTimeout());
}
}
ZookeeperSyncDataConfiguration 是一个配置类,初始化了一个zk客户端和一个数据同步服务ZookeeperSyncDataService。
- 进入ZookeeperSyncDataService,其构造函数如下:
public ZookeeperSyncDataService(final ZkClient zkClient, final PluginDataSubscriber pluginDataSubscriber,
final List<MetaDataSubscriber> metaDataSubscribers, final List<AuthDataSubscriber> authDataSubscribers) {
this.zkClient = zkClient;
this.pluginDataSubscriber = pluginDataSubscriber;
this.metaDataSubscribers = metaDataSubscribers;
this.authDataSubscribers = authDataSubscribers;
watcherData();
watchAppAuth();
watchMetaData();
}
服务设置了zk客户端,以及设置了插件数据和元数据等相关处理的订阅者,最下面的三个watch函数我们对watchMetaData进行分析。
4. watchMetaData
private void watchMetaData() {
final String metaDataPath = ZkPathConstants.META_DATA;
//获取元数据结点的所有子节点
List<String> childrenList = zkClientGetChildren(metaDataPath);
if (CollectionUtils.isNotEmpty(childrenList)) {
childrenList.forEach(children -> {
String realPath = buildRealPath(metaDataPath, children);
//将每个子节点进行缓存
cacheMetaData(zkClient.readData(realPath));
//给子节点设置监听处理函数
subscribeMetaDataChanges(realPath);
});
}
subscribeChildChanges(ConfigGroupEnum.APP_AUTH, metaDataPath, childrenList);
}
- subscribeMetaDataChanges函数是给客户端注册监听处理函数,具体如下:
private void subscribeMetaDataChanges(final String realPath) {
zkClient.subscribeDataChanges(realPath, new IZkDataListener() {
//节点变化后的处理
@Override
public void handleDataChange(final String dataPath, final Object data) {
//更新缓存
cacheMetaData((MetaData) data);
}
//结点删除后的处理
@SneakyThrows
@Override
public void handleDataDeleted(final String dataPath) {
final String realPath = dataPath.substring(ZkPathConstants.META_DATA.length() + 1);
MetaData metaData = new MetaData();
metaData.setPath(URLDecoder.decode(realPath, StandardCharsets.UTF_8.name()));
//删除缓存
unCacheMetaData(metaData);
}
});
}
验证时,在特定位置打上断点,前端元数据管理界面修改元数据配置,即可验证上述逻辑。
问题
- 前端直接新增的元数据貌似不能直接同步到bootstrap?点击同步按钮也不行?
- 前端批量删除元数据,boostrap报如下错误(尚未进行分析):
2021-01-30 03:15:16.686 ERROR 14344 --- [-localhost:2181] org.I0Itec.zkclient.ZkEventThread : Error handling event ZkEvent[Children of /soul/metaData changed sent to org.dromara.soul.sync.data.zookeeper.ZookeeperSyncDataService$$Lambda$543/1932866809@2e2f246]
java.lang.ClassCastException: org.dromara.soul.common.dto.MetaData cannot be cast to org.dromara.soul.common.dto.AppAuthData
at org.dromara.soul.sync.data.zookeeper.ZookeeperSyncDataService.lambda$12(ZookeeperSyncDataService.java:193) ~[classes/:na]
at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193) ~[na:1.8.0_60]
at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1374) ~[na:1.8.0_60]
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481) ~[na:1.8.0_60]
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471) ~[na:1.8.0_60]
at java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151) ~[na:1.8.0_60]
at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:174) ~[na:1.8.0_60]
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) ~[na:1.8.0_60]
at java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:418) ~[na:1.8.0_60]
at org.dromara.soul.sync.data.zookeeper.ZookeeperSyncDataService.lambda$11(ZookeeperSyncDataService.java:195) ~[classes/:na]
at org.I0Itec.zkclient.ZkClient$10.run(ZkClient.java:844) ~[zkclient-0.10.jar:na]
at org.I0Itec.zkclient.ZkEventThread.run(ZkEventThread.java:72) ~[zkclient-0.10.jar:na]