【ShenYu系列】ShenYu网关条件匹配的设计及原理分析

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,使用匹配策略工厂根据每一个SelectorDataMatchMode进行匹配。

    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);}
}

总结一下

  1. 获取指定的选择器,即selector。每一个selector都有对应的匹配策略和条件集合。
  2. 匹配策略决定了条件集合是and还是or的关系
  3. 每一个条件都有一个判断策略,根据操作符获取,有endWith类型的,有regex类型的。
  4. 每一个条件对应一种参数类型,参数类型决定了条件匹配的数据的来源,有从请求头中获取的,有从访问路径中获取的。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/108.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MySQL基础篇第3章(基本的SELECT语句)

文章目录 1、SQL概述1.1 SQL背景知识1.2 SQL分类 2、SQL语言的规则与规范2.1 基本规则2.2 SQL大小写规范 &#xff08;建议遵守&#xff09;2.3 注释2.4 命名规则2.5 数据导入指令 3、基本的SELECT语句3.0 SELECT...3.1 SELECT...FROM3.2 列的别名3.3 去除重复行3.4 空置参与运…

探究群体智能:使用Python实现粒子群优化(PSO)、萤火虫算法(FA)、布谷鸟搜索(CS)、蚁群优化(ACO)与人工蜂群(ABC)

在本篇文章中&#xff0c;我们将探索几种基于群体智能的优化算法&#xff0c;这些算法模拟了生物群体中出现的协同行为&#xff0c;并用以解决复杂的优化问题。具体来说&#xff0c;我们将探讨以下五种算法&#xff1a;粒子群优化&#xff08;PSO&#xff09;、萤火虫算法&…

OpenCV 入门教程: Sobel算子和Scharr算子

OpenCV 入门教程&#xff1a; Sobel 算子和 Scharr 算子 导语一、Sobel 算子二、Scharr 算子三、示例应用3.1 图像边缘检测3.2 边缘增强 总结 导语 在图像处理和计算机视觉领域&#xff0c;边缘检测是一项重要的任务。 Sobel 算子和 Scharr 算子是两种常用的边缘检测算子&…

MOVEit再现新漏洞,多个版本受影响

今年6月&#xff0c;文件共享工具MOVEit Transfer曾曝出SQL 注入漏洞&#xff0c;能让远程攻击者访问其数据库并执行任意代码。最近&#xff0c;MOVEit Transfer 母公司Progress Software又披露了三个新漏洞。 这三个漏洞分别是 CVE-2023-36932、CVE-2023-36933 和 CVE-2023-36…

云事业群CTO线技术晋升考核机试题-分布式专题-C 分布式任务调度

2023年&#xff08;Q3财年&#xff09;技术部门CTO线技术人员晋升考核机试题 分布式篇-C 分布式任务调度 *参考答案* 出题人&#xff1a;湖北TL田超凡答案制定&#xff1a;湖北TL田超凡 *****试卷启用前绝密**** 1 传统的定时任务存在那些缺点 答&#xff1a;传统定时任务…

window安装MongoDB

安装直接先去官网下载 Download MongoDB Community Server | MongoDB 安装后如下&#xff0c;我们直接双击运行&#xff0c; 这里记得选下面(可以自己选择安装盘符位置)&#xff0c;上面第一个会自动帮你安装到C盘&#xff0c;然后选择下一步 &#xff0c;这里勾选就会选择去自…

win10电脑出现网络问题时,如何解决?

我们的Windows可能会出现各种网络连接问题&#xff1a; 尝试连接Wi-Fi网络时出现错误&#xff1a;Windows无法连接到此网络&#xff1b;可以通过Wifi访问互联网&#xff0c;但通过电缆访问以太网却无法正常工作&#xff1b;尝试通过电缆连接互联网时出现错误&#xff1a; Wind…

MATLAB 之 文件操作

这里写目录标题 一、文件操作1. 文件打开与关闭1.1 fopen 函数1.2 fclose 函数 2. 文件的读写2.1 fscanf 函数2.2 fprintf 函数2.3 fgetl 与 fgets函数2.4 textscan 函数2.5 fread 函数2.6 fwrite 函数 3. 数据文件定位3.1 fseek 函数3.2 ftell 函数3.3 feof 函数3.4 ferror 函…

如何使用chatgpt写文章

最近&#xff0c;我身边的朋友都在说ChatGPT&#xff0c;说它是写作的神器&#xff0c;能够帮助他们创作出优质的文章。也许有些人会感到迷惑&#xff0c;什么是ChatGPT&#xff1f;如何使用它来写文章呢&#xff1f; 今天&#xff0c;我将为你揭秘一切&#xff01;不仅提供详…

NXP i.MX 6ULL工业开发板硬件说明书( ARM Cortex-A7,主频792MHz)

前 言 本文档主要介绍TLIMX6U-EVM评估板硬件接口资源以及设计注意事项等内容。 创龙科技TLIMX6U-EVM是一款基于NXP i.MX 6ULL的ARM Cortex-A7高性能低功耗处理器设计的评估板&#xff0c;由核心板和评估底板组成。核心板经过专业的PCB Layout和高低温测试验证&#xff0c;稳…

基于Springboot+vue的垃圾分类网站设计与实现

博主介绍&#xff1a; 大家好&#xff0c;我是一名在Java圈混迹十余年的程序员&#xff0c;精通Java编程语言&#xff0c;同时也熟练掌握微信小程序、Python和Android等技术&#xff0c;能够为大家提供全方位的技术支持和交流。 我擅长在JavaWeb、SSH、SSM、SpringBoot等框架…

Linux下软件安装的命令

源码安装 以源代码安装软件&#xff0c;每次都需要配置操作系统、配置编译参数、实际编译&#xff0c;最后还要依据个人喜好的方式来安装软件。这个过程很麻烦很累人。 RPM安装软件的默认路径: 注意&#xff1a; /etc 配置文件放置目录/usr/bin 一些可执行文件/usr/lib 一些程…

星辰秘典:探索Python项目的神秘力量——贪吃蛇

✨博主&#xff1a;命运之光 &#x1f338;专栏&#xff1a;星辰秘典&#xff1a;探索Python项目的神秘力量 &#x1f433;专栏&#xff1a;web开发&#xff08;html css js&#xff09; ❤️专栏&#xff1a;Java经典程序设计 ☀️博主的其他文章&#xff1a;点击进入博主的主…

Flutter GetX使用

下载地址&#xff1a; https://pub-web.flutter-io.cn/packages/get 导入依赖&#xff1a; dependencies:get: ^4.6.5导入头文件&#xff1a; import package:get/get.dart;使用 Obx() 、 GetX<T>() 创建全局数据 Counter&#xff0c;继承 GetxController&#xff1a…

JAVA - equal 和 == 等号比较

号 号比较的是栈中的数据 可以用于基本数据类型和引用类型 对于基本数据类型&#xff0c;数据存储在栈中&#xff0c;因此判断的是值是否相同 对于引用数据类型&#xff0c;栈中存储的是对象在堆中的引用地址&#xff0c;因此比较的是对象在栈中保存的引用&#xff08;地址&a…

基于SpringBoot+Hadoop+Vue的企业网盘系统

完整资料进入【数字空间】查看——baidu搜索"writebug" 1.1.1 选题的背景 随着信息网络技术的迅猛发展&#xff0c;云计算技术从它的概念提出已经开始在实际生产环境中使用了。大部分的东西都已经慢慢云端化&#xff0c;这种新型的技术也受到许多互联网人员的关注&a…

ARM CORETEX M0简介

ARM CORETEX M0简介 1. M0处理器简单框图 处理器内核&#xff1a;逻辑控制与运算 内部总线系统&#xff1a;单总线将处理器与外部的存储器和外部连接&#xff0c;进行数据交互&#xff08;冯诺依曼架构&#xff0c;数据与指令一起&#xff09; NVIC&#xff1a;嵌套向量中断控…

RabbitMQ部署

文章目录 RabbitMQ部署指南1.单机部署1.1.下载镜像1.2.安装MQ RabbitMQ部署指南 RabbitMQ是基于Erlang语言开发的开源消息通信中间件&#xff0c;官网地址&#xff1a;https://www.rabbitmq.com Erlang语言: 面向并发的编程语言&#xff0c;天生为了分布式系统而设计的 1.单机…

好用的网址6

PPT课件网&#xff1a;http://www.pptkj.net/ ImgUpscaler&#xff1a;AI Image Upscaler - Upscale Photo, Cartoons in Batch Free 加强图片 AI Draw&#xff1a;AI Draw | Convert Images to One-Line Drawings with AI ZToDoList&#xff1a;https://www.ztodolis…

elementUI 表格页面层级嵌套过多不及时刷新/错位的解决办法

1.强制刷新(可能会影响性能) //(嵌套过深需要强制刷新数据) forceUpdate() {this.$forceUpdate(); }, 2.页面重新布局(适用于表格错位等问题) 前提需要绑定: <el-form ref"table"></el-form> this.$refs["table"][0].doLayout(); //重新布局