soul-admin修改成zookeeper的数据同步方式
参考的是项目文档https://dromara.org/zh-cn/docs/soul/user-dataSync.html
记得先启动zookeeper,我是使用docker启动的,端口是2181。
1.在soul-bootstrap的pom.xml引入依赖
<!--soul data sync start use zookeeper-->
<dependency>
<groupId>org.dromara</groupId>
<artifactId>soul-spring-boot-starter-sync-data-zookeeper</artifactId>
<version>${last.version}</version>
</dependency>
2.如图1所示修改成zookeeper的数据同步格式
![图1](https://img-blog.csdnimg.cn/20210122014305362.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2E1MTEzMTAxMzI=,size_16,color_FFFFFF,t_70)
3.如图2修改soul-admin的配置文件
![](https://img-blog.csdnimg.cn/2021012201441771.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2E1MTEzMTAxMzI=,size_16,color_FFFFFF,t_70)
4.启动admin和bootstrap,查看zookeeper的数据,可以看到会插入一个soul的目录,文件如图3所示
![](https://img-blog.csdnimg.cn/20210122014100346.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2E1MTEzMTAxMzI=,size_16,color_FFFFFF,t_70)
源码分析
全局搜索ZookeeperSyncData会找到如图4的这几个文件。主要的就是ZookeeperSyncDataService和ZookeeperSyncDataConfiguration这两个文件。另外两个是测试文件。
![](https://img-blog.csdnimg.cn/20210122014853806.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2E1MTEzMTAxMzI=,size_16,color_FFFFFF,t_70)
ZookeeperSyncDataConfiguration一看就是配置类相关的,这里面注释写的很清楚,一个是注入数据同步的服务,另外一个是注册zkClient到spring的ioc容器。
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());
}
}
另外一个类是ZookeeperSyncDataService,实现了SyncDataService, AutoCloseable接口,但其实SyncDataService没有定义接口。
然后可以看到ZookeeperSyncDataService有很多方法。
我们试着关闭一个插件,然后看看调用了ZookeeperSyncDataService的watcherData()方法,原因是因为zkClient.subscribeChildChanges会触发。
private void watcherData() {
final String pluginParent = ZkPathConstants.PLUGIN_PARENT;
List<String> pluginZKs = zkClientGetChildren(pluginParent);
for (String pluginName : pluginZKs) {
watcherAll(pluginName);
}
zkClient.subscribeChildChanges(pluginParent, (parentPath, currentChildren) -> {
if (CollectionUtils.isNotEmpty(currentChildren)) {
for (String pluginName : currentChildren) {
watcherAll(pluginName);
}
}
});
}
打断点看到方法进到watcherAll这个方法。类似模板方法一样,查看了插件,选择器和规则有没有变化。
private void watcherAll(final String pluginName) {
watcherPlugin(pluginName);
watcherSelector(pluginName);
watcherRule(pluginName);
}
由于我们刚刚改变的插件,那么我们进入watcherPlugin(pluginName)这里看看
private void watcherPlugin(final String pluginName) {
String pluginPath = ZkPathConstants.buildPluginPath(pluginName);
//如果没有路径则注册
if (!zkClient.exists(pluginPath)) {
zkClient.createPersistent(pluginPath, true);
}
cachePluginData(zkClient.readData(pluginPath));
subscribePluginDataChanges(pluginPath, pluginName);
}
subscribePluginDataChanges方法如下实现了zkClient的两个方法,一个是handleDataChange更新操作,另外一个是handleDataDeleted删除操作
private void subscribePluginDataChanges(final String pluginPath, final String pluginName) {
zkClient.subscribeDataChanges(pluginPath, new IZkDataListener() {
@Override
public void handleDataChange(final String dataPath, final Object data) {
Optional.ofNullable(data)
.ifPresent(d -> Optional.ofNullable(pluginDataSubscriber).ifPresent(e -> e.onSubscribe((PluginData) d)));
}
@Override
public void handleDataDeleted(final String dataPath) {
final PluginData data = new PluginData();
data.setName(pluginName);
Optional.ofNullable(pluginDataSubscriber).ifPresent(e -> e.unSubscribe(data));
}
});
}