soul源码分析总结篇之插件化设计

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请求被转发时,主要是AbstractSoulPluginWebClientPlugin在起作用。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为例,插件的具体执行是AbstractSoulPluginexecute方法,那找一下调用关系(比如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.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值