Soul中各个模块是按照插件进行加载的,插件有对应的优先级,会按照优先级排序然后顺序进行执行对应的插件进行处理请求。今天就来探究下插件是如何进行加载的以及插件是如何进行调用的。
SoulConfiguration配置了SoulWebHandler,使用ObjectProvider加载插件列表,并按照order字段进行排序【后面的插件执行顺序是按照这个来的】
SoulWebHandler主要是用来接收前端发过来的请求,然后使用插件列表中的插件对进来的请求进行处理,处理使用了handle方法,handle方法会在DefaultSoulPluginChain的构造器中传入插件列表,其中,DefaultSoulPluginChain实现了SoulPluginChain接口,方便插件调用下一个插件,根据skip方法判断是否是需要跳过的【即非当前插件类型的请求不进行执行,直到找到对应的插件进行调用返回】
插件接口SoulPlugin,包含了执行,顺序,名称,是否需要跳过四个需要实现的方法
AbstractSoulPlugin实现了执行的方法【通用的执行方法】
插件链条的发起的地方在GlobalPlugin,根据传进来的ServerWebExchange获取必要的信息,构造SoulContext,SoulContext很关键,下面所有的插件都会用到。
比如DividePlugin
/**
*
* @param exchange exchange the current server exchange {@linkplain ServerWebExchange} 请求的信息,以及GlobalPlugin塞进来的SoulContext
* @param chain chain the current chain {@linkplain ServerWebExchange} 调用链路,所有的加载的插件
* @param selector selector {@linkplain SelectorData} 选择器
* @param rule rule {@linkplain RuleData} 规则
* @return
*/
@Override
protected Mono 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);
// 获取缓存的调用的url列表信息
final List 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);
}
// 获取ip
final String ip = Objects.requireNonNull(exchange.getRequest().getRemoteAddress()).getAddress().getHostAddress();
// 负载均衡选一个调用的url
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);
}
WebClientPlugin根据拼装的信息执行调用
@Override
public Mono execute(final ServerWebExchange exchange, final SoulPluginChain chain) {
final SoulContext soulContext = exchange.getAttribute(Constants.CONTEXT);
assert soulContext != null;
String urlPath = exchange.getAttribute(Constants.HTTP_URL);
if (StringUtils.isEmpty(urlPath)) {
Object error = SoulResultWrap.error(SoulResultEnum.CANNOT_FIND_URL.getCode(), SoulResultEnum.CANNOT_FIND_URL.getMsg(), null);
return WebFluxResultUtils.result(exchange, error);
}
long timeout = (long) Optional.ofNullable(exchange.getAttribute(Constants.HTTP_TIME_OUT)).orElse(3000L);
int retryTimes = (int) Optional.ofNullable(exchange.getAttribute(Constants.HTTP_RETRY)).orElse(0);
log.info("The request urlPath is {}, retryTimes is {}", urlPath, retryTimes);
HttpMethod method = HttpMethod.valueOf(exchange.getRequest().getMethodValue());
WebClient.RequestBodySpec requestBodySpec = webClient.method(method).uri(urlPath);
return handleRequestBody(requestBodySpec, exchange, timeout, retryTimes, chain);
}
查看下PluginEnum,RESPONSE是在最后一个,也就是返回结果的地方。
最后是调用WebClientResponsePlugin返回执行结果
@Override
public Mono execute(final ServerWebExchange exchange, final SoulPluginChain chain) {
return chain.execute(exchange).then(Mono.defer(() -> {
ServerHttpResponse response = exchange.getResponse();
ClientResponse clientResponse = exchange.getAttribute(Constants.CLIENT_RESPONSE_ATTR);
if (Objects.isNull(clientResponse)
|| response.getStatusCode() == HttpStatus.BAD_GATEWAY
|| response.getStatusCode() == HttpStatus.INTERNAL_SERVER_ERROR) {
Object error = SoulResultWrap.error(SoulResultEnum.SERVICE_RESULT_ERROR.getCode(), SoulResultEnum.SERVICE_RESULT_ERROR.getMsg(), null);
return WebFluxResultUtils.result(exchange, error);
}
if (response.getStatusCode() == HttpStatus.GATEWAY_TIMEOUT) {
Object error = SoulResultWrap.error(SoulResultEnum.SERVICE_TIMEOUT.getCode(), SoulResultEnum.SERVICE_TIMEOUT.getMsg(), null);
return WebFluxResultUtils.result(exchange, error);
}
response.setStatusCode(clientResponse.statusCode());
response.getCookies().putAll(clientResponse.cookies());
response.getHeaders().putAll(clientResponse.headers().asHttpHeaders());
return response.writeWith(clientResponse.body(BodyExtractors.toDataBuffers()));
}));
}