学习目标:
soul是如何实现热插拔的
学习内容:
1.soul数据维护总结的概述
2.soul是如何实现热插拔的
学习时间:
2021年1月30号 下午4点
学习产出:
1.soul数据维护总结的概述
前面学习了soul会吧数据维护在admin中,存储在数据库并同步更新到内存中,数据变更会通过配置的数据传递机制(zk,websock,http长轮询,nacos)传递给 soul bootstrap 中; soul bootstrap 吧数据存储在内存;今天主要学习 soul 网关数据如何加载到 soul bootstrap 运行时,以及 soul bootstrap 如何实现插件热插拔。
前面的学习当中,我们了解到了 soul bootstrap 侧通过引入 soul-spring-boot-starter-sync-data-center 中的 stater 后,在启动的配置文件中通过相应的配置属性开关来开启不同方式的配置同步。数据同步依赖的接口定义在了模块
soul-sync-data-api 中,分别定义了 AuthDataSubscriber,MetaDataSubscriber,PluginDataSubscriber,SyncDataService。SyncDataService的实现如(HttpSyncDataService)
SyncDataService 的各个实现类中就依赖了 AuthDataSubscriber,MetaDataSubscriber,PluginDataSubscriber 来完成数据的更新
public HttpSyncDataService(final HttpConfig httpConfig, final PluginDataSubscriber pluginDataSubscriber,
final List<MetaDataSubscriber> metaDataSubscribers, final List<AuthDataSubscriber> authDataSubscribers) {
this.factory = new DataRefreshFactory(pluginDataSubscriber, metaDataSubscribers, authDataSubscribers);
this.httpConfig = httpConfig;
this.serverList = Lists.newArrayList(Splitter.on(",").split(httpConfig.getUrl()));
this.httpClient = createRestTemplate();
this.start();
}
例如PluginDataSubscriber:
public interface PluginDataSubscriber {
/**
* On subscribe.
*
* @param pluginData the plugin data
*/
default void onSubscribe(PluginData pluginData) {
}
/**
* Un subscribe plugin data.
*
* @param pluginData the plugin data
*/
default void unSubscribe(PluginData pluginData) {
}
/**
* Refresh all plugin data.
*/
default void refreshPluginDataAll() {
}
/**
* Refresh plugin data self.
*
* @param pluginDataList the plugin data list
*/
default void refreshPluginDataSelf(List<PluginData> pluginDataList) {
}
/**
* On selector subscribe.
*
* @param selectorData the selector data
*/
default void onSelectorSubscribe(SelectorData selectorData) {
}
/**
* Un selector subscribe.
*
* @param selectorData the selector data
*/
default void unSelectorSubscribe(SelectorData selectorData) {
}
/**
* Refresh all selector data.
*/
default void refreshSelectorDataAll() {
}
/**
* Refresh selector data.
*
* @param selectorDataList the selector data list
*/
default void refreshSelectorDataSelf(List<SelectorData> selectorDataList) {
}
/**
* On rule subscribe.
*
* @param ruleData the rule data
*/
default void onRuleSubscribe(RuleData ruleData) {
}
/**
* On rule subscribe.
*
* @param ruleData the rule data
*/
default void unRuleSubscribe(RuleData ruleData) {
}
/**
* Refresh rule data.
*/
default void refreshRuleDataAll() {
}
/**
* Refresh rule data self.
*
* @param ruleDataList the rule data list
*/
default void refreshRuleDataSelf(List<RuleData> ruleDataList) {
}
PluginDataSubscriber中包含了3类数据的加载;
PluginData, SelectorData, RuleData;对每类数据的处理有 onSubscribe, unSubscribe, refreshXXXDataAll, refreshXXXDataSelf方法;
PluginDataSubscriber 的实现类 org.dromara.soul.plugin.base.cache.CommonPluginDataSubscriber 可以看到,对每类数据的处理最终使用了 org.dromara.soul.plugin.base.cache.BaseDataCache,这个类当中定义了 PluginData, SelectorData, RuleData 的缓存存储容器。
CommonPluginDataSubscriber 中的 onSubscribe, unSubscribe 系列方法还依赖与 org.dromara.soul.plugin.base.handler.PluginDataHandler 接口的实现,不同的插件有不同的 PluginDataHandler,PluginData, SelectorData, RuleData 定义了各个插件运行时的行为, soul bootstrap 通过依赖 plugin starter 就能集成到这些插件的行为。
public class CommonPluginDataSubscriber implements PluginDataSubscriber {
private final Map<String, PluginDataHandler> handlerMap;
/**
* Instantiates a new Common plugin data subscriber.
*
* @param pluginDataHandlerList the plugin data handler list
*/
public CommonPluginDataSubscriber(final List<PluginDataHandler> pluginDataHandlerList) {
this.handlerMap = pluginDataHandlerList.stream().collect(Collectors.toConcurrentMap(PluginDataHandler::pluginNamed, e -> e));
}
@Override
public void onSubscribe(final PluginData pluginData) {
subscribeDataHandler(pluginData, DataEventTypeEnum.UPDATE);
}
@Override
public void unSubscribe(final PluginData pluginData) {
subscribeDataHandler(pluginData, DataEventTypeEnum.DELETE);
}
@Override
public void refreshPluginDataAll() {
BaseDataCache.getInstance().cleanPluginData();
}
@Override
public void refreshPluginDataSelf(final List<PluginData> pluginDataList) {
if (CollectionUtils.isEmpty(pluginDataList)) {
return;
}
BaseDataCache.getInstance().cleanPluginDataSelf(pluginDataList);
}
@Override
public void onSelectorSubscribe(final SelectorData selectorData) {
subscribeDataHandler(selectorData, DataEventTypeEnum.UPDATE);
}
@Override
public void unSelectorSubscribe(final SelectorData selectorData) {
subscribeDataHandler(selectorData, DataEventTypeEnum.DELETE);
}
@Override
public void refreshSelectorDataAll() {
BaseDataCache.getInstance().cleanSelectorData();
}
@Override
public void refreshSelectorDataSelf(final List<SelectorData> selectorDataList) {
if (CollectionUtils.isEmpty(selectorDataList)) {
return;
}
BaseDataCache.getInstance().cleanSelectorDataSelf(selectorDataList);
}
@Override
public void onRuleSubscribe(final RuleData ruleData) {
subscribeDataHandler(ruleData, DataEventTypeEnum.UPDATE);
}
@Override
public void unRuleSubscribe(final RuleData ruleData) {
subscribeDataHandler(ruleData, DataEventTypeEnum.DELETE);
}
@Override
public void refreshRuleDataAll() {
BaseDataCache.getInstance().cleanRuleData();
}
@Override
public void refreshRuleDataSelf(final List<RuleData> ruleDataList) {
if (CollectionUtils.isEmpty(ruleDataList)) {
return;
}
BaseDataCache.getInstance().cleanRuleDataSelf(ruleDataList);
}
private <T> 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));
}
}
});
}
}
- soul是如何实现热插拔的
依赖的各个 plugin starter 的配置中,@ConditionalOnClass 注解的配置,配置会被 Spring 容器加载的条件是 classpath 下有对应的插件类信息,如果配置未被加载,插件处理相关的类的实例不会被加载到 soul bootstrap 运行时。
soul admin 侧可以管理插件,选择器,规则等信息的配置,这些数据维护后会被同步到 soul bootstrap 的缓存,以此来控制 soul bootstrap 的行为。