前言
在前一篇文章《Soul网关源码学习(9)- 请求解析 GlobalPlugin》 中,我们学习请求流入的第一个插件 GlobalPlugin 的源码,GlobalPlugin 处理完之后,请求就有可能因为协议的不同而流向不同的插件路线,而后面这些插件的实现有很大一部分都有着一个共同的抽象模板 AbstractSoulPlugin,所以在学习后面这些插件的源码之前,有必要先来了解一下 AbstractSoulPlugin 的实现。
AbstractSoulPlugin
AbstractSoulPlugin 顾名思义,是一个抽象模板类,SoulPlugin 的大部分实现都继承于它。它实现了 SoulPlugin#execute(ServerWebExchange, SoulPluginChain) 接口方法,提供了一些数据预处理的模板流程,并将真正的执行逻辑委派给子类的 doExecute(ServerWebExchange, SoulPluginChain, SelectorData, RuleData) 方法。
//下面代码,省略了具体的处理,只列出流程的关键节点
@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()) {
//处理选择器 selector 数据
final Collection<SelectorData> selectors = BaseDataCache.getInstance().obtainSelectorData(pluginName);
selectorLog(selectorData, pluginName);
...
//处理接口规则 rule
final List<RuleData> rules = BaseDataCache.getInstance().obtainRuleData(selectorData.getId());
...
//子类执行真正的处理逻辑
return doExecute(exchange, chain, selectorData, rule);
}
//间接递归 插件链的 execute 方法
return chain.execute(exchange);
}
从上面的代码中我们能梳理出代码流程中的几个主要节点,也是我们今天分析的要点:
- 获取插件数据 pluginData,判断其是否可用(加载 + 打开状态)。
- 获取 SelectorData 并且处理。
- 获取 Rule 集合并且处理。
- 委派子类方法 doExecute 执行真正的处理逻辑。
pluginData
PluginData 是描述插件的一些基本数据信息,在 soul admin 中的体现:
PluginData 会通过数据同步机制,从 soul admin 同步到网关本地内存中,因为网关获取到的数据都是本地缓存的副本。对于这一点,soul 网关所有配置信息都是一样的,也包括后面的 selectorData 和 Rule。
模板方法首先判断 PluginData 是否为空,为空则表示插件则没被加载,也就是没有添加相应的插件依赖;否则,再判断其是否打开,也就是上面图片中的 Status 是否处于 open 状态。如果两个条件满足了,才会往下执行,否则就结束当前插件处理,跳回插件链。
SelectorData
官方文档说法:选择器和规则是soul网关中最灵魂的东西。掌握好它,你可以对任何流量进行管理。
不过在AbstractSoulPlugin中并不涉及数据的使用,而只是实现对 SelectorData 的获取和筛选,真正的处理是交由子类方法 doExecute 来实现。
我们来看一下 SelectorData 再 admin 页面的直接体现,选择器列表:
上面图片中有个关键的状态 “open”,这个状态在代码中的使用(matchSelector 方法在会在模板方法 execute 中被调用):
private SelectorData matchSelector(final ServerWebExchange exchange, final Collection<SelectorData> selectors) {
return selectors.stream()
//这里会过滤掉 open 状态没激活的 selector
.filter(selector -> selector.getEnabled() &&
//这是根据 selector 详细信息的配置进行过滤
filterSelector(selector, exchange))
.findFirst().orElse(null);
}
选择器详细信息配置:
这里主要使用 MathType 和 Condtions 两项配置信息来进行过滤:
//对单个 selector 进行匹配
private Boolean filterSelector(final SelectorData selector, final ServerWebExchange exchange) {
if (selector.getType() == SelectorTypeEnum.CUSTOM_FLOW.getCode()) {
if (CollectionUtils.isEmpty(selector.getConditionList())) {
return false;
}
// selector.getMatchMode():对应上图的 MathType,匹配策略
// selector.getConditionList() :很明显就是 Conditions
return MatchStrategyUtils.match(selector.getMatchMode(), selector.getConditionList(), exchange);
}
return true;
}
Rule
紧接着是 Rule 的获取和筛选,其过程和上面的 Selector Data 非常相似。对于Rule 的具体配置,小伙伴们可以直接参考官方文档。
和上面一样,我们来看一下 Rule Data 在 Admin 界面的直观体现,Rule List:
Rule Detail:
结合上面的两幅图,我们再结合一下其筛选代码(该方法处在模板方法 executor 的调用链路里):
private Boolean filterRule(final RuleData ruleData, final ServerWebExchange exchange) {
// 第一个判断:列表中的 open 状态
// 第二个判断:条件匹配,和 selector data 的逻辑一样
return ruleData.getEnabled() && MatchStrategyUtils.match(ruleData.getMatchMode(), ruleData.getConditionDataList(), exchange);
}
doExecute
获取并且匹配完 SeletorData 和 Rule 集合后,就可以将它们传递给子类的 doExecutor 方法进行使用了。例如:DividePlugin、ApacheDubboPlugin、SofaPlugin、SpringCloudPlugin、HystrixPlugin、SentinelPlugin 等这些插件都是继承了 AbstractSoulPlugin 并且实现了该父类方法,这样子类就可以直接使用 Seletor 和 Rule 的相关数据了。
总结
这一章节,我们学习了模板类 AbstractSoulPlugin 的处理流程,知道其主要对三样数据进行了获取和过滤:PluginData、SelectorData 和 Rule,并将它们作为参数传递给子类方法 doExecutor 进行使用。后面,我们就开始对这些子类的具体实现进行分析。