Soul网关源码解析(七)Zookeeper同步数据数据同步配置启动时的同步admin的同步处理小结参考
通过上一节,我们知道了websocket数据同步的流程,本小节学习Zookeeper数据同步的流程。
数据同步配置
Admin与Bootstrap的配置参数相同,如下所示,支持的参数有Zk的url,Session超时时间,连接超时时间,指定序列化器:
soul :
sync:
zookeeper:
url: localhost:2181
sessionTimeout: 5000
connectionTimeout: 2000
程序如何知道启用zookeeper数据同步?这是因为在加载配置类时,下面这个配置类有个@ConditionalOnProperty(prefix = "soul.sync.zookeeper", name = "url")
,如果配置属性中存在soul.sync.zookeeper.url,那么就会初始化这个配置类,而这个配置类又需要加载ZookeeperSyncDataService类,这就是zookeeper同步数据的核心类。
@Configuration
@ConditionalOnClass(ZookeeperSyncDataService.class)
@ConditionalOnProperty(prefix = "soul.sync.zookeeper", name = "url")
@EnableConfigurationProperties(ZookeeperConfig.class)
@Slf4j
public class ZookeeperSyncDataConfiguration {
...
}
下面来看一下这个Bean构建时如何进行同步的,首先@Bean注释的方法,如果方法有参数,会先从Spring Bean容器里获取这些对象,如果没有,那么Spring就会进行构造,构造的前提是这些类有被spring注解标识(比如@Bean),希望由Spring容器管理对象生命周期,这里构建ZookeeperSyncDataService对象时,需要传入ZkClient对象,那就需要构建这个对象,构建ZkClient对象时,需要传入ZookeeperConfig对象,那Spring容器就会尝试构建这个对象,ZookeeperConfig对象会根据配置中前缀为soul.sync.zookeeper的值对其进行属性赋值并创建。
#ZookeeperSyncDataConfiguration
@Bean
public SyncDataService syncDataService(final ObjectProvider zkClient,
final ObjectProvider pluginSubscriber,
final ObjectProvider> metaSubscribers,
final ObjectProvider> authSubscribers) {
log.info("you use zookeeper sync soul data.......");
return new ZookeeperSyncDataService(zkClient.getIfAvailable(),
pluginSubscriber.getIfAvailable(),
metaSubscribers.getIfAvailable(Collections::emptyList),
authSubscribers.getIfAvailable(Collections::emptyList));
}
@Bean
public ZkClient zkClient(final ZookeeperConfig zookeeperConfig) {
return new ZkClient(zookeeperConfig.getUrl(),
zookeeperConfig.getSessionTimeout(),
zookeeperConfig.getConnectionTimeout());
}
#ZookeeperConfig
@Data
@ConfigurationProperties(prefix = "soul.sync.zookeeper")
public class ZookeeperConfig {
private String url;
private Integer sessionTimeout;
private Integer connectionTimeout;
private String serializer;
}
ZookeeperSyncDataService对象的构建除了常规的设置成员变量,还通过zk客户端,建立对插件,选择器,规则,认证以及元数据的监听,通过订阅节点事件,当发生事件时,能收到服务端发送来的事件信息。
#ZookeeperSyncDataService
public ZookeeperSyncDataService(final ZkClient zkClient,
final PluginDataSubscriber pluginDataSubscriber,
final List metaDataSubscribers,
final List authDataSubscribers) {
this.zkClient = zkClient;
this.pluginDataSubscriber = pluginDataSubscriber;
this.metaDataSubscribers = metaDataSubscribers;
this.authDataSubscribers = authDataSubscribers;
watcherData();
watchAppAuth();
watchMetaData();
}
private void watcherData() {
final String pluginParent = ZkPathConstants.PLUGIN_PARENT;
// 从zk获取节点列表
List pluginZKs = zkClientGetChildren(pluginParent);
for (String pluginName : pluginZKs) {
watcherAll(pluginName);
}
// 订阅节点变化通知
zkClient.subscribeChildChanges(pluginParent, (parentPath, currentChildren) -> {
if (CollectionUtils.isNotEmpty(currentChildren)) {
for (String pluginName : currentChildren) {
watcherAll(pluginName);
}
}
});
}
// 监听插件,插件内的选择器,插件内的规则
private void watcherAll(final String pluginName) {
watcherPlugin(pluginName);
watcherSelector(pluginName);
watcherRule(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);
}
// 订阅插件数据
private void cachePluginData(final PluginData pluginData) {
Optional.ofNullable(pluginData).flatMap(data -> Optional.ofNullable(pluginDataSubscriber)).ifPresent(e -> e.onSubscribe(pluginData));
}
#CommonPluginDataSubscriber
public void onSubscribe(final PluginData pluginData) {
subscribeDataHandler(pluginData, DataEventTypeEnum.UPDATE);
}
private void subscribeDataHandler(final T classData, final DataEventTypeEnum dataType) {
Optional.ofNullable(classData).ifPresent(data -> {
// 数据是否为插件数据
if (data instanceof PluginData) {
PluginData pluginData = (PluginData) data;
// 如果操作是更新,则更新缓存
if (dataType == DataEventTypeEnum.UPDATE) {
BaseDataCache.getInstance().cachePluginData(pluginData);
Optional.ofNullable(handlerMap.get(pluginData.getName())).ifPresent(handler -> handler.handlerPlugin(pluginData));
} else if (dataType == DataEventTypeEnum.DELETE) {
BaseDataCache.getInstance().removePluginData(pluginData);
// 尝试调用插件的处理器
Optional.ofNullable(handlerMap.get(pluginData.getName())).ifPresent(handler -> handler.removePlugin(pluginData));
}
} else if (data instanceof SelectorData) {
SelectorData selectorData = (SelectorData) data;
if (dataType == DataEventTypeEnum.UPDATE) {
BaseDataCache.getInstance().cacheSelectData(selectorData);
Optional.ofNullable(handlerMap.get(selectorData.getPluginName())).ifPresent(handler -> handler.handlerSelector(selectorData));
} else if (dataType == DataEventTypeEnum.DELETE) {
BaseDataCache.getInstance().removeSelectData(selectorData);
Optional.ofNullable(handlerMap.get(selectorData.getPluginName())).ifPresent(handler -> handler.removeSelector(selectorData));
}
} else if (data instanceof RuleData) {
RuleData ruleData = (RuleData) data;
if (dataType == DataEventTypeEnum.UPDATE) {
BaseDataCache.getInstance().cacheRuleData(ruleData);
Optional.ofNullable(handlerMap.get(ruleData.getPluginName())).ifPresent(handler -> handler.handlerRule(ruleData));
} else if (dataType == DataEventTypeEnum.DELETE) {
BaseDataCache.getInstance().removeRuleData(ruleData);
Optional.ofNullable(handlerMap.get(ruleData.getPluginName())).ifPresent(handler -> handler.removeRule(ruleData));
}
}
});
}
#ZookeeperSyncDataService
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));
}
});
}
这里DataChangedEventDispatcher实现了InitializingBean接口,会在admin启动初始化Bean时,执行如下函数,这个函数会获取所有DataChangedListener类的Bean对象,并存入List成员变量中。这里创建了Zk的监听器,并初始化了Zk节点数据,
#DataChangedEventDispatcher implements ApplicationListener, InitializingBean
@Override
public void afterPropertiesSet() {
Collection listenerBeans = applicationContext.getBeansOfType(DataChangedListener.class).values();
this.listeners = Collections.unmodifiableList(new ArrayList<>(listenerBeans));
}
#DataSyncConfiguration
@Configuration
@ConditionalOnProperty(prefix = "soul.sync.zookeeper", name = "url")
@Import(ZookeeperConfiguration.class)
static class ZookeeperListener {
@Bean
@ConditionalOnMissingBean(ZookeeperDataChangedListener.class)
public DataChangedListener zookeeperDataChangedListener(final ZkClient zkClient) {
return new ZookeeperDataChangedListener(zkClient);
}
@Bean
@ConditionalOnMissingBean(ZookeeperDataInit.class)
public ZookeeperDataInit zookeeperDataInit(final ZkClient zkClient, final SyncDataService syncDataService) {
return new ZookeeperDataInit(zkClient, syncDataService);
}
}
#ZookeeperDataInit implements CommandLineRunner
public void run(final String... args) {
String pluginPath = ZkPathConstants.PLUGIN_PARENT;
String authPath = ZkPathConstants.APP_AUTH_PARENT;
String metaDataPath = ZkPathConstants.META_DATA;
if (!zkClient.exists(pluginPath) && !zkClient.exists(authPath) && !zkClient.exists(metaDataPath)) {
syncDataService.syncAll(DataEventTypeEnum.REFRESH);
}
}
#SyncDataServiceImpl
public boolean syncAll(final DataEventTypeEnum type) {
// 认证数据同步
appAuthService.syncData();
List pluginDataList = pluginService.listAll();
// 插件数据同步刷新,DataEventTypeEnum为REFRESH
// 广播发布事件有变化,让各个监听器接收到数据变化,并作出各自的处理
eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.PLUGIN, type, pluginDataList));
List selectorDataList = selectorService.listAll();
eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.SELECTOR, type, selectorDataList));
List ruleDataList = ruleService.listAll();
eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.RULE, type, ruleDataList));
// 元数据数据同步
metaDataService.syncData();
return true;
}
#DataChangedEventDispatcher
当时事件发生,根据事件Key,调用不同监听器进行事件变更处理
public void onApplicationEvent(final DataChangedEvent event) {
for (DataChangedListener listener : listeners) {
switch (event.getGroupKey()) {
case APP_AUTH:
listener.onAppAuthChanged((List) event.getSource(), event.getEventType());
break;
case PLUGIN:
listener.onPluginChanged((List) event.getSource(), event.getEventType());
break;
case RULE:
listener.onRuleChanged((List) event.getSource(), event.getEventType());
break;
case SELECTOR:
listener.onSelectorChanged((List) event.getSource(), event.getEventType());
break;
case META_DATA:
listener.onMetaDataChanged((List) event.getSource(), event.getEventType());
break;
default:
throw new IllegalStateException("Unexpected value: " + event.getGroupKey());
}
}
}
#ZookeeperDataChangedListener
public void onPluginChanged(final List changed, final DataEventTypeEnum eventType) {
for (PluginData data : changed) {
final String pluginPath = ZkPathConstants.buildPluginPath(data.getName());
// delete 时,会将zk中的数据删除,包括插件的选择器和规则
if (eventType == DataEventTypeEnum.DELETE) {
deleteZkPathRecursive(pluginPath);
final String selectorParentPath = ZkPathConstants.buildSelectorParentPath(data.getName());
deleteZkPathRecursive(selectorParentPath);
final String ruleParentPath = ZkPathConstants.buildRuleParentPath(data.getName());
deleteZkPathRecursive(ruleParentPath);
continue;
}
//create or update
upsertZkNode(pluginPath, data);
}
}
//真实写入zk
private void upsertZkNode(final String path, final Object data) {
if (!zkClient.exists(path)) {
zkClient.createPersistent(path, true);
}
zkClient.writeData(path, data);
}
本小结以配置Zookeeper同步数据的方式入手,介绍了admin和bootstrap端配置要一致,接着走读了bootstrap启动时进行的zookeeper同步的流程,依据订阅zk节点的事件,当节点发生变化,做出相应的处理,最后介绍了一下admin这边启动时做的zookeeper数据同步的准备工作,并在发生变化时的处理流程,自此本小节结束。希望能帮到你,初识soul这样一个极致性能的网关项目。
Soul 官网