ShenYu
网关中用到了很多有趣的设计,我对其中的条件匹配的实现尤其感兴趣,所以研究一下具体实现的原理。我这边用到的shenyu
版本是2.6.0-SNAPSHOT
。
应用入口
原理拆解
AbstractShenyuPlugin#execute
,获取到SelectorData
集合,进行匹配。
public Mono<Void> execute(final ServerWebExchange exchange, final ShenyuPluginChain chain) {initCacheConfig();final String pluginName = named();PluginData pluginData = BaseDataCache.getInstance().obtainPluginData(pluginName);// early exitif (Objects.isNull(pluginData) || !pluginData.getEnabled()) {return chain.execute(exchange);}final String path = exchange.getRequest().getURI().getPath();List<SelectorData> selectors = BaseDataCache.getInstance().obtainSelectorData(pluginName);SelectorData selectorData = obtainSelectorDataCacheIfEnabled(path);// handle Selectorif (Objects.nonNull(selectorData) && StringUtils.isBlank(selectorData.getId())) {return handleSelectorIfNull(pluginName, exchange, chain);}if (Objects.isNull(selectorData)) {if (CollectionUtils.isEmpty(selectors)) {return handleSelectorIfNull(pluginName, exchange, chain);}Pair<Boolean, SelectorData> matchSelectorData = matchSelector(exchange, selectors);selectorData = matchSelectorData.getRight();if (Objects.isNull(selectorData)) {if (matchCacheConfig.getSelector().getSelectorEnabled() && matchSelectorData.getLeft()) {selectorData = new SelectorData();selectorData.setPluginName(pluginName);cacheSelectorData(path, selectorData);}return handleSelectorIfNull(pluginName, exchange, chain);} else {if (matchCacheConfig.getSelector().getSelectorEnabled() && matchSelectorData.getLeft()) {cacheSelectorData(path, selectorData);}}}printLog(selectorData, pluginName);if (Objects.nonNull(selectorData.getContinued()) && !selectorData.getContinued()) {// if continued, not match rulesreturn doExecute(exchange, chain, selectorData, defaultRuleData(selectorData));}List<RuleData> rules = BaseDataCache.getInstance().obtainRuleData(selectorData.getId());if (CollectionUtils.isEmpty(rules)) {return handleRuleIfNull(pluginName, exchange, chain);}RuleData ruleData = obtainRuleDataCache(path);if (Objects.nonNull(ruleData) && Objects.isNull(ruleData.getId())) {return handleRuleIfNull(pluginName, exchange, chain);}if (selectorData.getType() == SelectorTypeEnum.FULL_FLOW.getCode()) {//get lastRuleData rule = rules.get(rules.size() - 1);printLog(rule, pluginName);return doExecute(exchange, chain, selectorData, rule);} else {// lru map as L1 cache,the cache is enabled by default.// if the L1 cache fails to hit, using L2 cache based on trie cache.// if the L2 cache fails to hit, execute default strategy.if (Objects.isNull(ruleData)) {// L1 cache not exist data, try to get data through trie cacheruleData = trieMatchRule(exchange, selectorData, path);// trie cache fails to hit, execute default strategyif (Objects.isNull(ruleData)) {LOG.info("{} rule match path from default strategy", named());Pair<Boolean, RuleData> matchRuleData = matchRule(exchange, rules);ruleData = matchRuleData.getRight();if (matchRuleData.getLeft()) {ruleData = Optional.ofNullable(ruleData).orElse(RuleData.builder().pluginName(pluginName).matchRestful(false).build());cacheRuleData(path, ruleData);}}}}if (Objects.isNull(ruleData) || Objects.isNull(ruleData.getId())) {return handleRuleIfNull(pluginName, exchange, chain);}printLog(ruleData, pluginName);return doExecute(exchange, chain, selectorData, ruleData);}
AbstractShenyuPlugin#matchSelector
,使用匹配策略工厂根据每一个SelectorData
的MatchMode
进行匹配。
private Pair<Boolean, SelectorData> matchSelector(final ServerWebExchange exchange, final Collection<SelectorData> selectors) {List<SelectorData> filterCollectors = selectors.stream().filter(selector -> selector.getEnabled() && filterSelector(selector, exchange)).distinct().collect(Collectors.toList());if (filterCollectors.size() > 1) {return Pair.of(Boolean.FALSE, manyMatchSelector(filterCollectors));} else {return Pair.of(Boolean.TRUE, filterCollectors.stream().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 MatchStrategyFactory.match(selector.getMatchMode(), selector.getConditionList(), exchange);}return true;}
MatchStrategyFactory#match
,获取到匹配策略,有and策略和or策略。
public static boolean match(final Integer strategy, final List<ConditionData> conditionDataList, final ServerWebExchange exchange) {return newInstance(strategy).match(conditionDataList, exchange);}public static MatchStrategy newInstance(final Integer strategy) {String matchMode = MatchModeEnum.getMatchModeByCode(strategy);return ExtensionLoader.getExtensionLoader(MatchStrategy.class).getJoin(matchMode);}
or策略是其中一个匹配满足条件即可,判断条件用到了
@Join
public class OrMatchStrategy extends AbstractMatchStrategy implements MatchStrategy {@Overridepublic Boolean match(final List<ConditionData> conditionDataList, final ServerWebExchange exchange) {return conditionDataList.stream().anyMatch(condition -> PredicateJudgeFactory.judge(condition, buildRealData(condition, exchange)));}
}
and策略是所有条件都满足条件。
@Join
public class AndMatchStrategy extends AbstractMatchStrategy implements MatchStrategy {@Overridepublic Boolean match(final List<ConditionData> conditionDataList, final ServerWebExchange exchange) {return conditionDataList.stream().allMatch(condition -> PredicateJudgeFactory.judge(condition, buildRealData(condition, exchange)));}
}
构建匹配数据,用到了ParameterDataFactory
。
public abstract class AbstractMatchStrategy {/*** Build real data string.** @param condition the condition* @param exchange the exchange* @return the string*/public String buildRealData(final ConditionData condition, final ServerWebExchange exchange) {return ParameterDataFactory.builderData(condition.getParamType(), condition.getParamName(), exchange);}
}
ParameterDataFactory#builderData
,根据参数类型获取到对应的真实数据。
public static String builderData(final String paramType, final String paramName, final ServerWebExchange exchange) {return newInstance(paramType).builder(paramName, exchange);}public static ParameterData newInstance(final String paramType) {return ExtensionLoader.getExtensionLoader(ParameterData.class).getJoin(paramType);}
HeaderParameterData
,比如header参数,就是从header中获取数据。
@Join
public class HeaderParameterData implements ParameterData {@Overridepublic String builder(final String paramName, final ServerWebExchange exchange) {List<String> headers = exchange.getRequest().getHeaders().get(paramName);if (CollectionUtils.isEmpty(headers)) {return "";} return headers.get(0);}
}
URIParameterData
,就是获取访问地址路径。
@Join
public class URIParameterData implements ParameterData {@Overridepublic String builder(final String paramName, final ServerWebExchange exchange) {return exchange.getRequest().getURI().getPath();}
}
PredicateJudgeFactory#judge
,根据操作符获取处理器,进行判断
public static Boolean judge(final ConditionData conditionData, final String realData) {if (Objects.isNull(conditionData) || StringUtils.isBlank(realData)) {return false;}return newInstance(conditionData.getOperator()).judge(conditionData, realData);}public static PredicateJudge newInstance(final String operator) {return ExtensionLoader.getExtensionLoader(PredicateJudge.class).getJoin(processSpecialOperator(operator));}
EndsWithPredicateJudge
,判断是否以xxx为结尾。
@Join
public class EndsWithPredicateJudge implements PredicateJudge {@Overridepublic Boolean judge(final ConditionData conditionData, final String realData) {return realData.endsWith(conditionData.getParamValue().trim());}
}
RegexPredicateJudge
,使用正则匹配
@Join
public class RegexPredicateJudge implements PredicateJudge {@Overridepublic Boolean judge(final ConditionData conditionData, final String realData) {return Pattern.matches(conditionData.getParamValue().trim(), realData);}
}
总结一下
- 获取指定的选择器,即
selector
。每一个selector
都有对应的匹配策略和条件集合。 - 匹配策略决定了条件集合是
and
还是or
的关系 - 每一个条件都有一个判断策略,根据操作符获取,有
endWith
类型的,有regex
类型的。 - 每一个条件对应一种参数类型,参数类型决定了条件匹配的数据的来源,有从请求头中获取的,有从访问路径中获取的。