Soul閘道器原始碼探祕《六》 - 外掛鏈
阿新 • • 發佈:2021-01-21
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 請求的傳送。