前言
上一篇文章中,我们提到了SoulWebHandler和WebHandler。WebHandler是WeFlux编程的核心接口,而SoulWebHandler是自定义实现的一个Handler。也简单的介绍了SoulWebHandler的作用与构建时机等等。当然在SoulWebHandler里最核心的是handle方法,关于这个方法的调用是在DefaultWebFilterChain.filter中,代码如下:
@Override
public Mono<Void> filter(ServerWebExchange exchange) {
return Mono.defer(() ->
this.currentFilter != null && this.chain != null ?
invokeFilter(this.currentFilter, this.chain, exchange) :
this.handler.handle(exchange));
}
上面的代码中,首先判断当前的filter和chain是否为null,如果不为null,便执行invokeFilter方法,反之则调用handle方法,这里的this.handler便是WebHandler。后面有机会再介绍一下DefaultWebFilterChain。今天这篇是继续上一篇所说的,继续介绍plugin。
关于SoulPlugin
1、SoulPlugin的定义
这里的SoulPlugin是第一个接口,主要定义了execute方法,方法中的参数:ServerWebExchange、SoulPluginChain;然后就是getOrder方法,这个方法是为了获得plugin的顺序;接下来named方法,这个方法是为了获得插件名称;最后就是skip方法,这个方法是判断插件是否可以执行。代码如下:
public interface SoulPlugin {
/**
* Process the Web request and (optionally) delegate to the next
* [email protected] WebFilter} through the given [email protected] SoulPluginChain}.
*
* @param exchange the current server exchange
* @param chain provides a way to delegate to the next filter
* @return [email protected] Mono<Void>} to indicate when request processing is complete
*/
Mono<Void> execute(ServerWebExchange exchange, SoulPluginChain chain);
/**
* return plugin order .
* This attribute To determine the plugin execution order in the same type plugin.
*
* @return int order
*/
int getOrder();
/**
* acquire plugin name.
* this is plugin name define you must Provide the right name.
* if you impl AbstractSoulPlugin this attribute not use.
*
* @return plugin name.
*/
default String named() {
return "";
}
/**
* plugin is execute.
* if return true this plugin can not execute.
*
* @param exchange the current server exchange
* @return default false.
*/
default Boolean skip(ServerWebExchange exchange) {
return false;
}
}
2、SoulPlugin的实现
前面说了soulPlugin是一个接口,然后我们就可以看看关于这个接口的实现,可以发现这里的实现比较多,居然有31个之多。如图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HtiBe1fu-1611142903843)(https://uploader.shimo.im/f/aXtkOyd9LhRLYriZ.png!thumbnail?fileGuid=KR6RKQwVtrvKkTwd)]
这里笔者启动的是http插件,然后再DefaultSoulPluginChain的excute方法中打上断点,然后调用程序(见第二篇文章),便可以看到调用栈,如图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-87fWKSve-1611142903849)(https://uploader.shimo.im/f/QkIVH0kK8VLuCzzF.png!thumbnail?fileGuid=KR6RKQwVtrvKkTwd)]
这里的调用实现方是GlobalPlugin,那我们就以这个为例子,来看看里面的代码:
@Override
public Mono<Void> execute(final ServerWebExchange exchange, final SoulPluginChain chain) {
final ServerHttpRequest request = exchange.getRequest();
final HttpHeaders headers = request.getHeaders();
final String upgrade = headers.getFirst("Upgrade");
SoulContext soulContext;
if (StringUtils.isBlank(upgrade) || !"websocket".equals(upgrade)) {
soulContext = builder.build(exchange);
} else {
final MultiValueMap<String, String> queryParams = request.getQueryParams();
soulContext = transformMap(queryParams);
}
exchange.getAttributes().put(Constants.CONTEXT, soulContext);
return chain.execute(exchange);
}
上面这代代码的逻辑还是很清晰的,意思也很明确,但是这里有一个SoulContext的创建,这里的创建是基于SoulContextBuilder类的。那我们就来看看这个build方法的具体实现。
DefaultSoulContextBuilder的作用
1、DefaultSoulContextBuilder的实现
这是SoulContextBuilder接口的默认实现构建器,我们先来看看SoulContextBuilder:
public interface SoulContextBuilder {
SoulContext build(ServerWebExchange exchange);
}
这个接口只是定义一个build方法,那就DefaultSoulContextBuilder的实现,代码如下:
public class DefaultSoulContextBuilder implements SoulContextBuilder {
@Override
public SoulContext build(final ServerWebExchange exchange) {
final ServerHttpRequest request = exchange.getRequest();
String path = request.getURI().getPath();
MetaData metaData = MetaDataCache.getInstance().obtain(path);
if (Objects.nonNull(metaData) && metaData.getEnabled()) {
exchange.getAttributes().put(Constants.META_DATA, metaData);
}
return transform(request, metaData);
}
/**
* ServerHttpRequest transform RequestDTO .
*
* @param request [email protected] ServerHttpRequest}
* @return RequestDTO request dto
*/
private SoulContext transform(final ServerHttpRequest request, final MetaData metaData) {
final String appKey = request.getHeaders().getFirst(Constants.APP_KEY);
final String sign = request.getHeaders().getFirst(Constants.SIGN);
final String timestamp = request.getHeaders().getFirst(Constants.TIMESTAMP);
SoulContext soulContext = new SoulContext();
String path = request.getURI().getPath();
soulContext.setPath(path);
if (Objects.nonNull(metaData) && metaData.getEnabled()) {
if (RpcTypeEnum.SPRING_CLOUD.getName().equals(metaData.getRpcType())) {
setSoulContextByHttp(soulContext, path);
soulContext.setRpcType(metaData.getRpcType());
} else if (RpcTypeEnum.DUBBO.getName().equals(metaData.getRpcType())) {
setSoulContextByDubbo(soulContext, metaData);
} else if (RpcTypeEnum.SOFA.getName().equals(metaData.getRpcType())) {
setSoulContextBySofa(soulContext, metaData);
} else if (RpcTypeEnum.TARS.getName().equals(metaData.getRpcType())) {
setSoulContextByTars(soulContext, metaData);
} else {
setSoulContextByHttp(soulContext, path);
soulContext.setRpcType(RpcTypeEnum.HTTP.getName());
}
} else {
setSoulContextByHttp(soulContext, path);
soulContext.setRpcType(RpcTypeEnum.HTTP.getName());
}
soulContext.setAppKey(appKey);
soulContext.setSign(sign);
soulContext.setTimestamp(timestamp);
soulContext.setStartDateTime(LocalDateTime.now());
Optional.ofNullable(request.getMethod()).ifPresent(httpMethod -> soulContext.setHttpMethod(httpMethod.name()));
return soulContext;
}
private void setSoulContextByDubbo(final SoulContext soulContext, final MetaData metaData) {
soulContext.setModule(metaData.getAppName());
soulContext.setMethod(metaData.getServiceName());
soulContext.setRpcType(metaData.getRpcType());
soulContext.setContextPath(metaData.getContextPath());
}
private void setSoulContextBySofa(final SoulContext soulContext, final MetaData metaData) {
soulContext.setModule(metaData.getAppName());
soulContext.setMethod(metaData.getServiceName());
soulContext.setRpcType(metaData.getRpcType());
soulContext.setContextPath(metaData.getContextPath());
}
private void setSoulContextByTars(final SoulContext soulContext, final MetaData metaData) {
soulContext.setModule(metaData.getServiceName());
soulContext.setMethod(metaData.getMethodName());
soulContext.setRpcType(metaData.getRpcType());
soulContext.setContextPath(metaData.getContextPath());
}
private void setSoulContextByHttp(final SoulContext soulContext, final String path) {
String contextPath = "/";
String[] splitList = StringUtils.split(path, "/");
if (splitList.length != 0) {
contextPath = contextPath.concat(splitList[0]);
}
String realUrl = path.substring(contextPath.length());
soulContext.setContextPath(contextPath);
soulContext.setModule(contextPath);
soulContext.setMethod(realUrl);
soulContext.setRealUrl(realUrl);
}
}
这个类比较长,但是因为主入口是build方法,那就从build入口,首先是获取ServerHttpRequest,然后是获取path,然后就是构建元数据,最后就是转换数据。在transform方法中主要是根据元数据的类型来调用相关了类,这里有spring cloud、dubbo、sofa、tras、http,不过这几个上下文设置的方法都大体类似。
2、DefaultSoulContextBuilder的构建方法
不过在执行完DefaultSoulContextBuilder中过的build方法后,便会进入到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);
}
这个方法首先是获取插件名称,然后获取插件数据,紧接着判断plugindata是否为null且是否已启用,两个条件有一个不符合便执行chain.execute(exchange)方法,否则进入判断。
3、匹配选择器
在if代码块中,首先根据插件名获取选择器集合,再根据选择器集合获取选择器数据,这里所调用的是matchSelector方法,代码如下:
private SelectorData matchSelector(final ServerWebExchange exchange, final Collection<SelectorData> selectors) {
return selectors.stream()
.filter(selector -> selector.getEnabled() && filterSelector(selector, exchange))
.findFirst().orElse(null);
}
private Boolean filterSelector(final SelectorData selector, final ServerWebExchange exchange) {
if (selector.getType() == SelectorTypeEnum.CUSTOM_FLOW.getCode()) {
if (CollectionUtils.isEmpty(selector.getConditionList())) {
return false;
}
return MatchStrategyUtils.match(selector.getMatchMode(), selector.getConditionList(), exchange);
}
return true;
}
这段代码在过滤的时候,是根据选择器是否开启来进行过滤的,然后filterSelector方法,在这个方法中主要是调用MatchStrategyUtils的match方法是。那就来看看这个类。
4、MatchStrategyUtils.match
public class MatchStrategyUtils {
/**
* Match boolean.
*
* @param strategy the strategy
* @param conditionDataList the condition data list
* @param exchange the exchange
* @return the boolean
*/
public static boolean match(final Integer strategy, final List<ConditionData> conditionDataList, final ServerWebExchange exchange) {
String matchMode = MatchModeEnum.getMatchModeByCode(strategy);
MatchStrategy matchStrategy = ExtensionLoader.getExtensionLoader(MatchStrategy.class).getJoin(matchMode);
return matchStrategy.match(conditionDataList, exchange);
}
}
上面的代码意思是很明显的,这里根据策略来进行选择调用的。
总结
后面的代码调用还是比较复杂的,就先进行到这里,下一篇文章还会继续接着这个来进行进一步的剖析。
本篇文章首先从SoulPlugin的定义与实现来讲解的,然后简单的分析了一下DefaultSoulContextBuilder的实现,最后讲了一下选择器的匹配,但是只讲了部分,后面继续深入讲一下匹配的代码。
原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/1289.html