Soul 网关源码阅读(九)插件配置加载初探
Soul 网关源码阅读(九)插件配置加载初探
简介
今日来探索一下插件的初始化,及相关的配置的加载
源码 Debug
插件初始化
首先来到我们非常熟悉的插件链调用的类: SoulWebHandler ,在其中的 DefaultSoulPluginChain ,我们看到 plugins 是通过构造函数传入的
我们来跟踪一下是谁传入的,在 SoulWebHandler 的 public 那一行打上断点,重启后跟踪调用栈
public final class SoulWebHandler implements WebHandler {
private final List<SoulPlugin> plugins;
// 在这里也看到plugins是通过构造函数传入的,在public这一行打上断点,然后重启
public SoulWebHandler(final List<SoulPlugin> plugins) {
this.plugins = plugins;
String schedulerType = System.getProperty("soul.scheduler.type", "fixed");
if (Objects.equals(schedulerType, "fixed")) {
int threads = Integer.parseInt(System.getProperty(
"soul.work.threads", "" + Math.max((Runtime.getRuntime().availableProcessors() << 1) + 1, 16)));
scheduler = Schedulers.newParallel("soul-work-threads", threads);
} else {
scheduler = Schedulers.elastic();
}
}
@Override
public Mono<Void> handle(@NonNull final ServerWebExchange exchange) {
MetricsTrackerFacade.getInstance().counterInc(MetricsLabelEnum.REQUEST_TOTAL.getName());
Optional<HistogramMetricsTrackerDelegate> startTimer = MetricsTrackerFacade.getInstance().histogramStartTimer(MetricsLabelEnum.REQUEST_LATENCY.getName());
// 传入 plugins 到 DefaultSoulPluginChain
return new DefaultSoulPluginChain(plugins).execute(exchange).subscribeOn(scheduler)
.doOnSuccess(t -> startTimer.ifPresent(time -> MetricsTrackerFacade.getInstance().histogramObserveDuration(time)));
}
private static class DefaultSoulPluginChain implements SoulPluginChain {
private int index;
private final List<SoulPlugin> plugins;
// 通过构造函数得到 plugins
DefaultSoulPluginChain(final List<SoulPlugin> plugins) {
this.plugins = plugins;
}
@Override
public Mono<Void> execute(final ServerWebExchange exchange) {
return Mono.defer(() -> {
if (this.index < plugins.size()) {
SoulPlugin plugin = plugins.get(this.index++);
Boolean skip = plugin.skip(exchange);
if (skip) {
return this.execute(exchange);
}
return plugin.execute(exchange, this);
}
return Mono.empty();
});
}
}
}
我这有点慢,重启等以后后,我们查看上一个调用,来到了 SoulConfiguration
通过类的大致信息可以看到是 Spring 之类的加载机制。通过前面文章的分析中,大致应该就是配置了 auto configuration ,然后启动的时候 Spring 进行加载的
public class SoulConfiguration {
@Bean("webHandler")
public SoulWebHandler soulWebHandler(final ObjectProvider<List<SoulPlugin>> plugins) {
List<SoulPlugin> pluginList = plugins.getIfAvailable(Collections::emptyList);
final List<SoulPlugin> soulPlugins = pluginList.stream()
.sorted(Comparator.comparingInt(SoulPlugin::getOrder)).collect(Collectors.toList());
soulPlugins.forEach(soulPlugin -> log.info("load plugin:[{}] [{}]", soulPlugin.named(), soulPlugin.getClass().getName()));
return new SoulWebHandler(soulPlugins);
}
}
大致就知道是 Spring 自动加载的,通过 AutoConfiguration 机制,下面就是 Spring 相关了,这里就不继续跟踪了
配置初始化
在上一篇的文章分析中,我们分析了路由匹配的大致细节,需要选择器和规则的配置,下面我们来跟踪一下这些配置的初始化
首先进入路由匹配核心类: AbstractSoulPlugin ,其中进行 pluginData 、selectorData、rules 的获取,如下面代码注释:
public abstract class AbstractSoulPlugin implements SoulPlugin {
@Override
public Mono<Void> execute(final ServerWebExchange exchange, final SoulPluginChain chain) {
String pluginName = named();
// 获取插件数据
final PluginData pluginData = BaseDataCache.getInstance().obtainPluginData(pluginName);
if (pluginData != null && pluginData.getEnabled()) {
// 根据插件名称,获取选择器数据
final Collection<SelectorData> selectors = BaseDataCache.getInstance().obtainSelectorData(pluginName);
if (CollectionUtils.isEmpty(selectors)) {
return handleSelectorIsNull(pluginName, exchange, chain);
}
final SelectorData selectorData = matchSelector(exchange, selectors);
if (Objects.isNull(selectorData)) {
return handleSelectorIsNull(pluginName, exchange, chain);
}
selectorLog(selectorData, pluginName);
// 根据选择器的id,获取其规则数据
final List<RuleData> rules = BaseDataCache.getInstance().obtainRuleData(selectorData.getId());
if (CollectionUtils.isEmpty(rules)) {
return handleRuleIsNull(pluginName, exchange, chain);
}
RuleData rule;
if (selectorData.getType() == SelectorTypeEnum.FULL_FLOW.getCode()) {
rule = rules.get(rules.size() - 1);
} else {
rule = matchRule(exchange, rules);
}
if (Objects.isNull(rule)) {
return handleRuleIsNull(pluginName, exchange, chain);
}
ruleLog(rule, pluginName);
return doExecute(exchange, chain, selectorData, rule);
}
return chain.execute(exchange);
}
}
我们到这个类: BaseDataCache 看一下,看到它有两个数据的 Map 存储:插件数据、选择器数据、规则数据
public final class BaseDataCache {
/**
* pluginName -> PluginData.
*/
private static final ConcurrentMap<String, PluginData> PLUGIN_MAP = Maps.newConcurrentMap();
/**
* pluginName -> SelectorData.
*/
private static final ConcurrentMap<String, List<SelectorData>> SELECTOR_MAP = Maps.newConcurrentMap();
/**
* selectorId -> RuleData.
*/
private static final ConcurrentMap<String, List<RuleData>> RULE_MAP = Maps.newConcurrentMap();
}
我们发现它是一个静态单例,而插件数据的配置是使用下面的函数,我们在函数上打上断点,看看是谁调用了它,它的调用栈是怎么样的
public final class BaseDataCache {
public void cachePluginData(final PluginData pluginData) {
Optional.ofNullable(pluginData).ifPresent(data -> PLUGIN_MAP.put(data.getName(), data));
}
}
在 public 这一行打上断点,重启,顺利进入上面的函数,我们查看下 pluginData ,还挺简单,大致如下
PluginData(id=1, name=sign, config=null, role=1, enabled=false)
我们跟踪其调用栈,其调用链的类和函数如下,类在调用栈的顺序也是对应的
public class CommonPluginDataSubscriber implements PluginDataSubscriber {
// 在这个里面触发 subscribeDataHandler , subscribeDataHandler 触发 DaseDataCache的调用
@Override
public void onSubscribe(final PluginData pluginData) {
subscribeDataHandler(pluginData, DataEventTypeEnum.UPDATE);
}
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));
}
}
});
}
}
// 这个类中触发了上面单个插件的配置,并且这个类拿到了所有的插件数据
public class PluginDataHandler extends AbstractDataHandler<PluginData> {
@Override
protected void doRefresh(final List<PluginData> dataList) {
// 触发上面的调用
pluginDataSubscriber.refreshPluginDataSelf(dataList);
dataList.forEach(pluginDataSubscriber::onSubscribe);
}
}
// 继续跟下去,看看datalist的来源
public abstract class AbstractDataHandler<T> implements DataHandler {
@Override
public void handle(final String json, final String eventType) {
List<T> dataList = convert(json);
if (CollectionUtils.isNotEmpty(dataList)) {
DataEventTypeEnum eventTypeEnum = DataEventTypeEnum.acquireByName(eventType);
switch (eventTypeEnum) {
case REFRESH:
case MYSELF:
// 触发上面函数的调用
doRefresh(dataList);
break;
case UPDATE:
case CREATE:
doUpdate(dataList);
break;
case DELETE:
doDelete(dataList);
break;
default:
break;
}
}
}
}
// 来到了websocket,进行跟
public class WebsocketDataHandler {
public void executor(final ConfigGroupEnum type, final String json, final String eventType) {
ENUM_MAP.get(type).handle(json, eventType);
}
}
// 在这个类中,看到插件数据列表是从websocket来的,那就是从Soul-Admin那边获取来到的数据
public final class SoulWebsocketClient extends WebSocketClient {
public void onMessage(final String result) {
handleResult(result);
}
@SuppressWarnings("ALL")
private void handleResult(final String result) {
WebsocketData websocketData = GsonUtils.getInstance().fromJson(result, WebsocketData.class);
ConfigGroupEnum groupEnum = ConfigGroupEnum.acquireByName(websocketData.getGroupType());
String eventType = websocketData.getEventType();
String json = GsonUtils.getInstance().toJson(websocketData.getData());
websocketDataHandler.executor(groupEnum, json, eventType);
}
}
看下 result 的大致内容:也就是一些简单的配置
{
"groupType": "PLUGIN",
"eventType": "MYSELF",
"data": [{
"id": "1",
"name": "sign",
"role": 1,
"enabled": false
}, {
"id": "9",
"name": "hystrix",
"role": 0,
"enabled": false
}]
}
结合路由匹配的关键函数,好像它的作用就是用来判断是否开启和根据 name 获得选择器
public abstract class AbstractSoulPlugin implements SoulPlugin {
@Override
public Mono<Void> execute(final ServerWebExchange exchange, final SoulPluginChain chain) {
String pluginName = named();
final PluginData pluginData = BaseDataCache.getInstance().obtainPluginData(pluginName);
// 用于判断是否开启
if (pluginData != null && pluginData.getEnabled()) {
.......
}
}
}
插件数据这一块的流程定位下来比较轻松,毕竟前面流程也梳理差不多了
选择器和规则数据的初始化流程,这里就不进行定位了,逻辑和插件数据基本一样,这里就不赘述了
总结
今天分析了插件的初始化和相关配置的初始化
插件的初始化是使用 Spring 的自动配置加载机制进行实现了,结合使用插件都需要引入哪些 start 的依赖
插件数据、选择器数据、规则数据的初始化,都是从 Websocket(或者同步通信模块)那边过来的,接收到数据以后,进行配置加载到本地
我们还看到一些删除和更新相关的操作,这些属于后面的数据同步分析了,这里先放一放
Soul 网关源码分析文章列表
Github
掘金
版权声明: 本文为 InfoQ 作者【萧】的原创文章。
原文链接:【http://xie.infoq.cn/article/42358239b1ddbdb51a0b3c631】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
萧
还未添加个人签名 2018.09.09 加入
代码是门手艺活,也是门艺术活
评论