开启zookeeper数据同步
1.soul-bootstrap引入soul-spring-boot-starter-sync-data-zookeeper
依赖:
<!--soul data sync start use zookeeper-->
<dependency>
<groupId>org.dromara</groupId>
<artifactId>soul-spring-boot-starter-sync-data-zookeeper</artifactId>
<version>2.2.0</version>
</dependency>
2.在soul-bootstrap中添加zookeeper的配置:
zookeeper:
url: localhost:2181
sessionTimeout: 5000
connectionTimeout: 2000
3.soul-admin中开启zk同步数据
soul:
database:
dialect: mysql
init_script: "META-INF/schema.sql"
sync:
# websocket:
# enabled: true
zookeeper:
url: localhost:2181
sessionTimeout: 5000
connectionTimeout: 2000
zookeeper 数据同步原理
基于 zookeeper 的同步原理很简单,主要是依赖 zookeeper
的 watch 机制,soul-web
会监听配置的节点,soul-admin
在启动的时候,会将数据全量写入 zookeeper
,后续数据发生变更时,会增量更新 zookeeper
的节点,与此同时,soul-bootstrap
中引入的soul-spring-boot-starter-sync-data-zookeeper
会监听配置信息的节点,一旦有信息变更时,会更新本地缓存。
soul-admin 通过 zkClient 发送数据变化?
ZookeeperDataChangedListener
类实现了 DataChangedListener
接口,监听数据的变化,并通过zkClient将数据的变化增量更新到 zookeeper的节点.
public void onApplicationEvent(final DataChangedEvent event) {
for (DataChangedListener listener : listeners) {
switch (event.getGroupKey()) {
case APP_AUTH:
listener.onAppAuthChanged((List<AppAuthData>) event.getSource(), event.getEventType());
break;
case PLUGIN:
listener.onPluginChanged((List<PluginData>) event.getSource(), event.getEventType());
break;
case RULE:
listener.onRuleChanged((List<RuleData>) event.getSource(), event.getEventType());
break;
case SELECTOR:
listener.onSelectorChanged((List<SelectorData>) event.getSource(), event.getEventType());
break;
case META_DATA:
listener.onMetaDataChanged((List<MetaData>) event.getSource(), event.getEventType());
break;
default:
throw new IllegalStateException("Unexpected value: " + event.getGroupKey());
}
}
}
Zookeeper 监听器的初始化:
/**
* The type Zookeeper listener.
*/
@Configuration
@ConditionalOnProperty(prefix = "soul.sync.zookeeper", name = "url")
@Import(ZookeeperConfiguration.class)
static class ZookeeperListener {
/**
* Config event listener data changed listener.
*
* @param zkClient the zk client
* @return the data changed listener
*/
@Bean
@ConditionalOnMissingBean(ZookeeperDataChangedListener.class)
public DataChangedListener zookeeperDataChangedListener(final ZkClient zkClient) {
return new ZookeeperDataChangedListener(zkClient);
}
/**
* Zookeeper data init zookeeper data init.
*
* @param zkClient the zk client
* @param syncDataService the sync data service
* @return the zookeeper data init
*/
@Bean
@ConditionalOnMissingBean(ZookeeperDataInit.class)
public ZookeeperDataInit zookeeperDataInit(final ZkClient zkClient, final SyncDataService syncDataService) {
return new ZookeeperDataInit(zkClient, syncDataService);
}
}
soul-bootstrap 订阅zk数据的变化并处理
soul-spring-boot-starter-sync-data-zookeeper
作为 starter 被 soul-bootstrap 引入。
spring.factories 文件中指定了自动配置类 ZookeeperSyncDataConfiguration:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.dromara.soul.spring.boot.sync.data.zookeeper.ZookeeperSyncDataConfiguration
ZookeeperSyncDataConfiguration
中自动装载bean时,创建了 zookeeper
客户端对象,以及 ZookeeperSyncDataService
对象,
ZookeeperSyncDataService
会监听 zookeeper
中选择器、规则、元数据、用户权限等数据的变更。如何监听?----通过zookeeper
客户端订阅zookeeper
数据的变化,实现监听并处理变化的数据.
// 订阅zookeeper数据的变化实现监听
public void subscribeDataChanges(String path, IZkDataListener listener) {
synchronized(this._dataListener) {
Set<IZkDataListener> listeners = (Set)this._dataListener.get(path);
if (listeners == null) {
listeners = new CopyOnWriteArraySet();
this._dataListener.put(path, listeners);
}
((Set)listeners).add(listener);
}
this.watchForData(path);
LOG.debug("Subscribed data changes for " + path);
}
总结
参考:Apache ZooKeeper Watcher 机制源码解释
ZooKeeper 允许客户端向服务端注册一个 Watcher 监听,当服务端的一些指定事件触发了这个 Watcher,那么就会向指定客户端发送一个事件通知来实现分布式的通知功能。
ZooKeeper 的 Watcher 机制主要包括客户端线程、客户端 WatchManager 和 ZooKeeper 服务器三部分。在具体工作流程上,简单地讲,客户端在向 ZooKeeper 服务器注册 Watcher 的同时,会将 Watcher 对象存储在客户端的 WatchManager 中。当 ZooKeeper 服务器端触发 Watcher 事件后,会向客户端发送通知,客户端线程从 WatchManager 中取出对应的 Watcher 对象来执行回调逻辑