soul源码分析总结篇之插件化设计
说明
如果没有看之前文章的朋友,请至少先看阅读源码准备与soul基础
本文将包括如下内容:
- soul用插件实现了哪些功能?
- soul如何实现插件化设计?
Soul用插件实现了哪些功能?
插件是Soul的灵魂。
从架构图上就可以看出,soul
主要功能都是通过插件来实现的,比如监控、各种请求的转发(HTTP、Dubbo、SpringCloud、Sofa等)、限流与熔断、WAF等等,用户也可以自定义插件来扩展soul
。
截止到2021.1.26,目前支持的插件如下(参见soul
源码中的soul-plugin
模块):
Soul如何实现插件化设计?
1. 如何实现一个插件?
回忆一下,在http插件的使用与soul插件工作流程分析中,有分析过,divide
插件的工作流程,通过divide
处理一个请求的日志我们知道,HTTP请求被转发时,主要是AbstractSoulPlugin
和WebClientPlugin
在起作用。DividePlugin
插件的继承关系如下:
插件的实现采用模板方法设计模式,AbstractSoulPlugin
作为模板、规定了插件的主要流程,具体业务逻辑由继承该类的每种子类(插件)来实现。
AbstractSoulPlugin
的核心逻辑在execute
方法:
@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);
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()) {
//get last
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);
}
在该方法中可以看到’Mono’这种返回值,这是因为soul
中使用了反应式编程,引入了reactor-spring
,有关反应式编程又是一大块内容,请读者自行搜索。
此处execute
方法的逻辑并不复杂:
1. 根据当前插件名称在缓存中找到插件数据pluginData
2. if pluginData存在 且 可用
根据插件名称从缓存中找到对应selector
根据selector拿到所有规则rules,找到当前请求所匹配的那个规则rule
执行该规则(`doExecute`方法,具体逻辑取决于是哪个插件,比如HTTP的需要看DividePlugin的doExecute)
3. 执行后续的插件处理逻辑(责任链)
每个继承了AbstractSoulPlugin
的插件,都必须实现doExecute
方法,以便实现自己的处理逻辑。
2. 插件如何串起来调用、形成插件链?
还是以DividePlugin
为例,插件的具体执行是AbstractSoulPlugin
的execute
方法,那找一下调用关系(比如IDEA的’Find Usage’,或是Eclipse类似功能),可以知道是SoulWebHandler
中的内部类DefaultSoulPluginChain
类调用了插件链:
private static class DefaultSoulPluginChain implements SoulPluginChain {
private int index;
/**
所有插件。
请求过来之后,将遍历插件、走每个插件的处理逻辑。
*/
private final List<SoulPlugin> plugins;
/**
* Instantiates a new Default soul plugin chain.
*
* @param plugins the plugins
*/
DefaultSoulPluginChain(final List<SoulPlugin> plugins) {
this.plugins = plugins;
}
/**
* Delegate to the next {@code WebFilter} in the chain.
*
* @param exchange the current server exchange
* @return {@code Mono<Void>} to indicate when request handling is complete
*/
@Override
public Mono<Void> execute(final ServerWebExchange exchange) {
/*
反应式编程的写法,Mono.defer会创建一个数据源
*/
return Mono.defer(() -> {
if (this.index < plugins.size()) {
SoulPlugin plugin = plugins