1. 程式人生 > 其它 >Soul閘道器原始碼探祕《六》 - 外掛鏈

Soul閘道器原始碼探祕《六》 - 外掛鏈

技術標籤:原始碼探祕java

Soul閘道器原始碼探祕《六》 - 外掛鏈

外掛抽象類模版方法

今天主要探究某一個外掛的處理流程。首先來看一下AbstractPlugin提供的模版方法execute中做了什麼。

public Mono<Void> execute(final ServerWebExchange exchange, final SoulPluginChain chain) {
        /** 1. 獲取外掛名稱 */
        String pluginName = named();
        /** 2. 從快取中獲取該外掛的資訊 */
        final
PluginData pluginData = BaseDataCache.getInstance().obtainPluginData(pluginName); /** 3. 如果快取中沒有該外掛資訊或者該外掛沒有被開啟,將流程轉交至外掛鏈中下一個外掛 */ if (pluginData != null && pluginData.getEnabled()) { /** 4. 從快取中獲取外掛的所有選擇器資訊 */ final Collection<SelectorData> selectors =
BaseDataCache.getInstance().obtainSelectorData(pluginName); if (CollectionUtils.isEmpty(selectors)) { return handleSelectorIsNull(pluginName, exchange, chain); } /** 5. 為當前請求匹配相應的選擇器 */ final SelectorData selectorData = matchSelector(exchange,
selectors); if (Objects.isNull(selectorData)) { return handleSelectorIsNull(pluginName, exchange, chain); } /** 6. 輸出匹配成功的選擇器日誌資訊 */ selectorLog(selectorData, pluginName); /** 7. 從快取中獲取外掛的所有規則 */ final List<RuleData> rules = BaseDataCache.getInstance().obtainRuleData(selectorData.getId()); if (CollectionUtils.isEmpty(rules)) { return handleRuleIsNull(pluginName, exchange, chain); } RuleData rule; /** 8. 為當前請求匹配相應的規則 */ 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); } /** 9. 輸出匹配成功的規則日誌資訊 */ ruleLog(rule, pluginName); /** 10. 執行外掛中相應選擇器和規則的具體邏輯 */ return doExecute(exchange, chain, selectorData, rule); } return chain.execute(exchange); }

可以看到,在抽象類提供的模版方法中,為當前請求匹配了選擇器和規則,並轉交給外掛實現類執行後面的邏輯。

外掛的特有流程

AbstractPlugin中提供抽象方法doExecute給子類去實現各自特有的處理邏輯。

此次以DividePlugin為例來檢視後續流程。

@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);
        /** 1. 通過選擇器ID獲取所有提供服務的列表資訊 */
        final List<DivideUpstream> upstreamList = UpstreamCacheManager.getInstance().findUpstreamListBySelectorId(selector.getId());
        /** 2. 如果提供服務的列表為空,則輸出錯誤日誌,並規範化 response 輸出。PS:這段錯誤輸出邏輯可以提取成新的方法 */
        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);
        }
        /** 3. 獲取請求的IP */
        final String ip = Objects.requireNonNull(exchange.getRequest().getRemoteAddress()).getAddress().getHostAddress();
        /** 4. 從規則中設定的負載均衡演算法來計算真實去轉發的服務 */
        DivideUpstream divideUpstream = LoadBalanceUtils.selector(upstreamList, ruleHandle.getLoadBalance(), ip);
        /** 5. 如果經過負載均衡後轉發的服務為空,則輸出錯誤日誌,並規範化 response 輸出。PS:這段錯誤輸出邏輯依然可以提取成新的方法 */
        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
        /** 6. 生成真實請求的資訊並設定到 exchange 中 */
        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());
        /** 7. 轉發給外掛鏈上的下一個外掛繼續進行處理 */
        return chain.execute(exchange);
    }

DividePlugin中通過負載均衡演算法計算出實際要轉發的服務,並生成了轉發的服務資訊設定到設定到 exchange 中。然後轉發給外掛鏈上的下一個外掛繼續進行處理。

exchange 中設定好真實轉發的服務後,最終總得傳送出去。通過假設性原則,查詢soul-plugin-httpclient/src/main/java/org/dromara/soul/plugin/httpClient/檢視,果然發現在WebClientPlugin裡定義了它的 order

/** 意味著 WebClientPlugin 外掛總會在 DividePlugin 之後執行 */
public int getOrder() {
        return PluginEnum.DIVIDE.getCode() + 1;
    }

WebClientPlugin 中的 execute 中,實際轉發了請求。

public Mono<Void> execute(final ServerWebExchange exchange, final SoulPluginChain chain) {
        final SoulContext soulContext = exchange.getAttribute(Constants.CONTEXT);
        assert soulContext != null;
        /** 1. 獲取請求地址 */
        String urlPath = exchange.getAttribute(Constants.HTTP_URL);
        // some other code
        }
        /** 2. 獲取超市時間 */
        long timeout = (long) Optional.ofNullable(exchange.getAttribute(Constants.HTTP_TIME_OUT)).orElse(3000L);
        /** 3. 獲取重試次數 */
        int retryTimes = (int) Optional.ofNullable(exchange.getAttribute(Constants.HTTP_RETRY)).orElse(0);
        log.info("The request urlPath is {}, retryTimes is {}", urlPath, retryTimes);
        // some other code
        /** 4. 實際發起請求 */
        return handleRequestBody(requestBodySpec, exchange, timeout, retryTimes, chain);
    }

可以看到,WebClientPlugin外掛從 exchange 裡取出實際請求地址、超時時間以及重試次數,並完成了 HTTP 請求的傳送。