[Soul 源碼之旅] 1.8 Soul插件初體驗(yàn) (Divide -> 基礎(chǔ)流程)

1.8.2.1 基礎(chǔ)類

Soul 中充滿了各種各樣的插件,插件極大地豐富了 Soul 的功能,而且插件支持熱插拔,可擴(kuò)展新極高,Soul 大方部分功能都是基于插件進(jìn)行實(shí)現(xiàn)的。我們先來(lái)看一下 Soul 插件的抽象接口 SoulPlugin 的繼承關(guān)系:


image.png

我們可以看到 SoulPlugin 下又非常多的實(shí)現(xiàn)類,我們?cè)倏匆幌?DividePlugin 的繼承情況。


image.png

DividePlugin 繼承了AbstractSoulPlugin ,AbstractSoulPlugin 再實(shí)現(xiàn)了 SoulPlugin 接口。我們先看一下 SoulPlugin 有哪些方法:
image.png
  • execute 插件到執(zhí)行方法,這里傳入 Exchange 即各個(gè)插件之間進(jìn)行通信到橋梁, Chain 插件鏈。
  • getOrder 返回各個(gè)插件到執(zhí)行順序。我們看 SoulConfiguration 這里是Soul 的核心代碼,這里配置了對(duì)應(yīng)的WebHandler ,它是所有插件的起點(diǎn)。我們接下來(lái)將 webFulx 會(huì)講到。WebHandler 會(huì)根據(jù)Order 由小到大排列,定義各個(gè)插件的處理順序。
@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);
   }
  • name 返回插件名。
  • skip 定義是否需要跳過(guò)該插件。
    我們接著看 AbstractSoulPlugin 的方法。


    image.png

    我們先來(lái)看一下最重要的 Excute規(guī)則,這是個(gè) Default 方法,但做完 Selector 和 Rule 的匹配后,我們直接執(zhí)行 doExecute 方法,這個(gè)方法需要各個(gè)子類實(shí)現(xiàn)。

    @Override
    public Mono<Void> execute(final ServerWebExchange exchange, final SoulPluginChain chain) {
        String pluginName = named();
        // 獲取數(shù)據(jù)
        final PluginData pluginData = BaseDataCache.getInstance().obtainPluginData(pluginName);
        // 判斷是否開(kāi)啟插件
        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);
            // 獲取規(guī)則
            final List<RuleData> rules = BaseDataCache.getInstance().obtainRuleData(selectorData.getId());
            if (CollectionUtils.isEmpty(rules)) {
                return handleRuleIsNull(pluginName, exchange, chain);
            }
            RuleData rule;
            // 查找規(guī)則,假如是全局匹配則直接取最后一個(gè)規(guī)則
            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);
            // 真正執(zhí)行規(guī)則
            return doExecute(exchange, chain, selectorData, rule);
        }
        return chain.execute(exchange);
    }

我們接著看 DividePlugin 做了啥,其主要是向數(shù)據(jù)交換區(qū)塞入了一部分?jǐn)?shù)據(jù)。包括最終訪問(wèn)的地址,超時(shí)時(shí)間,重試次數(shù)。

    @Override
    protected Mono<Void> doExecute(final ServerWebExchange exchange, final SoulPluginChain chain, final SelectorData selector, final RuleData rule) {
        // 獲取上下文存根
        final SoulContext soulContext = exchange.getAttribute(Constants.CONTEXT);
        assert soulContext != null;
        final DivideRuleHandle ruleHandle = GsonUtils.getInstance().fromJson(rule.getHandle(), DivideRuleHandle.class);
        // 獲取下游服務(wù)列表
        final List<DivideUpstream> upstreamList = UpstreamCacheManager.getInstance().findUpstreamListBySelectorId(selector.getId());
        if (CollectionUtils.isEmpty(upstreamList)) {
            log.error("divide upstream configuration error: {}", rule.toString());
            Object error = SoulResultWrap.error(SoulResultEnum.CANNOT_FIND_URL.getCode(), SoulResultEnum.CANNOT_FIND_URL.getMsg(), null);
            return WebFluxResultUtils.result(exchange, error);
        }
        final String ip = Objects.requireNonNull(exchange.getRequest().getRemoteAddress()).getAddress().getHostAddress();
        // 根據(jù)負(fù)載均衡策略篩選下游服務(wù)器列表
        DivideUpstream divideUpstream = LoadBalanceUtils.selector(upstreamList, ruleHandle.getLoadBalance(), ip);
        if (Objects.isNull(divideUpstream)) {
            log.error("divide has no upstream");
            Object error = SoulResultWrap.error(SoulResultEnum.CANNOT_FIND_URL.getCode(), SoulResultEnum.CANNOT_FIND_URL.getMsg(), null);
            return WebFluxResultUtils.result(exchange, error);
        }
        // set the http url
        String domain = buildDomain(divideUpstream);
        String realURL = buildRealURL(domain, soulContext, exchange);
        exchange.getAttributes().put(Constants.HTTP_URL, realURL);
        // set the http timeout
        exchange.getAttributes().put(Constants.HTTP_TIME_OUT, ruleHandle.getTimeout());
        exchange.getAttributes().put(Constants.HTTP_RETRY, ruleHandle.getRetry());
        return chain.execute(exchange);
    }

最終調(diào)用了 chain.execute(exchange) 方法,我們?cè)賮?lái)看看調(diào)用鏈?zhǔn)窃趺磳?shí)現(xiàn)的。DefaultSoulPluginChain 它是 WebHandler 的子類,實(shí)現(xiàn)了 SoulPluginChain 接口,其主要實(shí)現(xiàn)了 execute 方法

@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();
            });
        }

這里使用了 Mono.defer 區(qū)別于 Mono.just 是立即執(zhí)行 just 里的 function ,defer 是延遲加載的,當(dāng)每次請(qǐng)求進(jìn)來(lái)都會(huì)生成一個(gè) DefaultSoulPluginChain ,這里記錄了當(dāng)情處理走到了那個(gè) plugin 。
最后我們看一下 SoulWebHandler ,他是所有WebFlux 請(qǐng)求的處理類。

    @Bean("webHandler")
    public SoulWebHandler soulWebHandler(final ObjectProvider<List<SoulPlugin>> plugins) {
        List<SoulPlugin> pluginList = plugins.getIfAvailable(Collections::emptyList);
        // 對(duì)插件進(jìn)行排序
        final List<SoulPlugin> soulPlugins = pluginList.stream()
                .sorted(Comparator.comparingInt(SoulPlugin::getOrder)).collect(Collectors.toList());
        // 調(diào)用插件鏈
        soulPlugins.forEach(soulPlugin -> log.info("load plugin:[{}] [{}]", soulPlugin.named(), soulPlugin.getClass().getName()));
        return new SoulWebHandler(soulPlugins);
    }

首先它對(duì)所有對(duì)插件進(jìn)行排序,然后插件鏈,進(jìn)行鏈?zhǔn)秸{(diào)度。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容