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 exit
if (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 Selector
if (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 rules
return 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 last
RuleData 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 cache
ruleData = trieMatchRule(exchange, selectorData, path);
// trie cache fails to hit, execute default strategy
if (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 {
@Override
public 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 {
@Override
public 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 {
@Override
public 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 {
@Override
public 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 {
@Override
public Boolean judge(final ConditionData conditionData, final String realData) {
return realData.endsWith(conditionData.getParamValue().trim());
}
}
RegexPredicateJudge
,使用正则匹配
@Join
public class RegexPredicateJudge implements PredicateJudge {
@Override
public Boolean judge(final ConditionData conditionData, final String realData) {
return Pattern.matches(conditionData.getParamValue().trim(), realData);
}
}
总结一下
- 获取指定的选择器,即
selector
。每一个selector
都有对应的匹配策略和条件集合。 - 匹配策略决定了条件集合是
and
还是or
的关系 - 每一个条件都有一个判断策略,根据操作符获取,有
endWith
类型的,有regex
类型的。 - 每一个条件对应一种参数类型,参数类型决定了条件匹配的数据的来源,有从请求头中获取的,有从访问路径中获取的。