spring cloud gateway集成sentinel并扩展支持restful api进行url粒度的流量治理
sentinel集成网关支持restful接口进行url粒度的流量治理
- 前言
- 使用网关进行总体流量治理(sentinel版本:1.8.6)
- 1、cloud gateway添加依赖:
- 2、sentinel配置
- 3、网关类型项目配置
- 4、通过zk事件监听刷新上报api分组信息
- 1、非网关项目上报api分组信息
- 2、网关添加监听事件
- 3、网关监听事件处理
- 5、sentinel控制台启动
前言
sentinel作为开源的微服务、流量治理组件,在对restful接口的支持上,在1.7之后才开始友好起来,对于带有@PathVariable的restful接口未作支持,在sentinel中/api/{id}这样的接口,其中/api/1与/api/2会被当做两个不同的接口处理,因此很难去做类似接口的流量治理,但在之后,sentinel团队已经提供了响应的csp扩展依赖,下文将会逐步讲述如何通过sentinel扩展来支持相应的服务流量治理
使用网关进行总体流量治理(sentinel版本:1.8.6)
这里选型为spring cloud gateway,而sentinel也对spring cloud gateway做了特殊照顾
1、cloud gateway添加依赖:
<!-- alibaba封装的sentinel的starter --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-sentinel</artifactId><version>2021.1</version></dependency><!-- alibaba封装的sentinel支持网关的starter --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId><version>2021.1</version></dependency><!-- 此包即为sentinel提供的扩展支持restful接口的依赖 --><dependency><groupId>com.alibaba.csp</groupId><artifactId>sentinel-spring-webmvc-adapter</artifactId><version>1.8.0</version></dependency>
上述需要重点关注的是sentinel-spring-webmvc-adapter包,此依赖是支持restful接口的关键,不需要我们自己改造。
2、sentinel配置
spring:cloud:sentinel:transport:#sentinel控制台地址dashboard: 1.1.1.1:8600#sentinel通信端口,默认为8179,被占用会继续扫描,一般固定起来port: 8700#项目所在服务器ipclient-ip: 2.2.2.2#心跳启动eager: true
client-ip在某些情况下不配置会出现sentinl控制台页面只有首页,服务一直注册不上去的情况,如果出现这种情况一定要配置上,如果没有这种情况,client-IP可以不配置,同时上述配置的这些ip端口都需要连通。
3、网关类型项目配置
/*** @classDesc:* @author: cyjer* @date: 2023/1/30 9:53*/
@SpringBootApplication
@EnableCaching
@Slf4j
public class SiriusApplication {public static void main(String[] args) {System.getProperties().setProperty("csp.sentinel.app.type", "1");SpringApplication.run(SiriusApplication.class, args);log.info("<<<<<<<<<<启动成功>>>>>>>>>>");}}
如果是网关类型的项目,需要配置csp.sentinel.app.type= 1,普通项目与网关项目,在控制台上所展示和可使用的功能是不同的
4、通过zk事件监听刷新上报api分组信息
通过将接口分组按照不同粒度,如controller粒度,和具体api接口粒度,通过zookeeper修改数据监听的方式,通过网关监听该事件,实现将api分组信息写入到sentinel中。
1、非网关项目上报api分组信息
/*** @classDesc: 扫描项目接口上报api* @author: cyjer* @date: 2023/2/10 13:46*/
@Configuration
@Slf4j
@Order(1)
@RequiredArgsConstructor
public class ApiDefinitionReporter implements BeanPostProcessor, CommandLineRunner, Constraint {private final List<ApiSiriusDefinition> apiSiriusDefinitionList = new ArrayList<>();private final GatewayServiceProperties gatewayServiceProperties;private final Environment environment;private final static char JTR = '/';private final static String PASS = "/**";private final static String APPLICATION_NAME = "spring.application.name";private final static String CONTEXT_PATH = "server.servlet.context-path";private final static List<String> PASS_LIST = Arrays.asList("swaggerWelcome", "basicErrorController", "swaggerConfigResource", "openApiResource");private final ZookeeperService zookeeperService;@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {// url访问路径为:访问基地址basePath+classMappingPath+methodPathif (!gatewayServiceProperties.isAutoReportAndRegister() || PASS_LIST.contains(beanName)) {return bean;}Class<?> beanClass = bean.getClass();Class<?> targetClass = AopUtils.getTargetClass(bean);//判断类上有无controller注解 spring代理类需用spring的注解扫描工具类查找RestController restController = AnnotationUtils.findAnnotation(beanClass, RestController.class);Controller controller = AnnotationUtils.findAnnotation(beanClass, Controller.class);//没有注解直接跳过扫描if (null == controller && null == restController) {return bean;}String applicationName = this.getApplicationName();//项目访问基地址String basePath = this.getBasePath();//如果类上有controller注解再查找requestMapping注解RequestMapping requestMapping = AnnotationUtils.findAnnotation(beanClass, RequestMapping.class);String classMappingPath = this.getClassMappingPath(requestMapping);//按照controller分组上报if (StringUtils.isNotBlank(classMappingPath)) {String controllerGroupPath = basePath + classMappingPath + PASS;ApiSiriusDefinition controllerGroup = new ApiSiriusDefinition();controllerGroup.setGatewayId(gatewayServiceProperties.getGatewayId());controllerGroup.setResource("服务:" + applicationName + ",控制器:" + targetClass.getSimpleName() + ",路径:" + controllerGroupPath);controllerGroup.setUrlPath(controllerGroupPath);apiSiriusDefinitionList.add(controllerGroup);}//查找类中所有方法,进行遍历Method[] methods = targetClass.getMethods();for (Method method : methods) {//查找方法上RequestMapping注解String methodPath = "";String requestType = "";RequestMapping methodRequestMapping = AnnotationUtils.findAnnotation(method, RequestMapping.class);if (methodRequestMapping != null) {String[] value = methodRequestMapping.value();RequestMethod[] requestMethods = methodRequestMapping.method();if (value.length == 0) {if (requestMethods.length == 0) {return bean;}RequestMethod requestMethod = requestMethods[0];requestType = requestMethod.name();if (requestMethod.equals(RequestMethod.POST)) {PostMapping postMapping = AnnotationUtils.findAnnotation(method, PostMapping.class);methodPath = this.joinMethodPath(postMapping.value());} else if (requestMethod.equals(RequestMethod.GET)) {GetMapping getMapping = AnnotationUtils.findAnnotation(method, GetMapping.class);methodPath = this.joinMethodPath(getMapping.value());} else if (requestMethod.equals(RequestMethod.DELETE)) {DeleteMapping deleteMapping = AnnotationUtils.findAnnotation(method, DeleteMapping.class);methodPath = this.joinMethodPath(deleteMapping.value());} else if (requestMethod.equals(RequestMethod.PATCH)) {PatchMapping patchMapping = AnnotationUtils.findAnnotation(method, PatchMapping.class);methodPath = this.joinMethodPath(patchMapping.value());} else if (requestMethod.equals(RequestMethod.PUT)) {PutMapping putMapping = AnnotationUtils.findAnnotation(method, PutMapping.class);methodPath = this.joinMethodPath(putMapping.value());}}ApiSiriusDefinition apiSiriusDefinition = new ApiSiriusDefinition();String urlPath = basePath + classMappingPath + methodPath;apiSiriusDefinition.setUrlPath(urlPath);apiSiriusDefinition.setRequestType(requestType);apiSiriusDefinition.setGatewayId(gatewayServiceProperties.getGatewayId());apiSiriusDefinition.setResource("服务:" + applicationName + ",请求类型:" + requestType + ",路径:" + urlPath);apiSiriusDefinitionList.add(apiSiriusDefinition);}}return bean;}private String joinMethodPath(String[] value) {if (value.length != 0) {String str = this.trimStrWith(value[0], JTR);return JTR + str;}return "";}private String getContextPath() {String contextPath = environment.getProperty(CONTEXT_PATH);contextPath = this.trimStrWith(contextPath, JTR);return StringUtils.isBlank(contextPath) ? "" : contextPath;}public String getApplicationName() {String applicationName = environment.getProperty(APPLICATION_NAME);applicationName = this.trimStrWith(applicationName, JTR);return StringUtils.isBlank(applicationName) ? "" : applicationName;}private String getBasePath() {String contextPath = this.getContextPath();String applicationName = this.getApplicationName();if (StringUtils.isBlank(contextPath)) {return JTR + applicationName;}return JTR + applicationName + JTR + contextPath;}private String getClassMappingPath(RequestMapping requestMapping) {if (null != requestMapping) {String requestMappingUrl = requestMapping.value().length == 0 ? "" : requestMapping.value()[0];requestMappingUrl = this.trimStrWith(requestMappingUrl, JTR);return JTR + requestMappingUrl;}return "";}public String trimStrWith(String str, char trimStr) {if (StringUtils.isBlank(str)) {return str;}int st = 0;int len = str.length();char[] val = str.toCharArray();while ((st < len) && (val[st] <= trimStr)) {st++;}while ((st < len) && (val[len - 1] <= trimStr)) {len--;}return ((st > 0) || (len < str.length())) ? str.substring(st, len) : str;}@Overridepublic void run(String... args) {if (StringUtils.isBlank(this.getApplicationName())) {throw new RuntimeException(APPLICATION_NAME + " should not be null");}if (!apiSiriusDefinitionList.isEmpty()) {log.info("<<<<< start to report api information to api governance platform >>>>>");try {zookeeperService.create(API_DEFINITION + SPLIT + getApplicationName(), JSONArray.toJSONString(apiSiriusDefinitionList));zookeeperService.update(API_DEFINITION + SPLIT + getApplicationName(), JSONArray.toJSONString(apiSiriusDefinitionList));} catch (Exception e) {log.error("reported api information failed,stack info:", e);}log.info("<<<<< successfully reported api information >>>>>");}}}
通过扫描项目下的controller和相应的mapping注解中的属性拼接出url来,通过zk来更新节点数据
2、网关添加监听事件
zk操作查看另一篇文章zookeeper相关操作
/*** @classDesc: 网关核心应用* @author: cyjer* @date: 2023/1/30 9:53*/
@Component
@Slf4j
@RequiredArgsConstructor
public class GatewayApplication implements ApplicationListener<ContextRefreshedEvent> {private final GatewayServiceProperties properties;private final ApiDefinitionService apiDefinitionService;private final ZookeeperService zookeeperService;private final ApiGroupProcesser apiGroupProcesser;@Overridepublic void onApplicationEvent(ContextRefreshedEvent event) {//拉取api governance platform 信息apiDefinitionService.refreshApiGovernanceInfo(properties.getGatewayId());log.info("<<<<<<<<<<刷新api分组信息完成>>>>>>>>>>");zookeeperService.create(Constraint.API_DEFINITION, "init");zookeeperService.addWatchChildListener(Constraint.API_DEFINITION, apiGroupProcesser);log.info("<<<<<<<<<<api上报监听器配置完成>>>>>>>>>>");}
}
通过事件处理,首先启动时刷新api信息,同时尝试初始化zk节点,然后注册监听watch。
3、网关监听事件处理
/*** @classDesc: api分组上报* @author: cyjer* @date: 2023/2/10 11:13*/
@Slf4j
@Component
public class ApiGroupProcesser extends AbstractChildListenerProcess implements ApiDefinitionConstraint {@Resourceprivate RedisTemplate<String, Object> redisTemplate;@Resourceprivate GatewayServiceProperties gatewayServiceProperties;@Overridepublic void process(CuratorFramework curatorFramework, PathChildrenCacheEvent cacheEvent) {ChildData data = cacheEvent.getData();if (Objects.nonNull(data) && cacheEvent.getType().equals(PathChildrenCacheEvent.Type.CHILD_UPDATED)) {log.info("<<<<<<<<<<上报api分组到sentinel>>>>>>>>>>");String path = data.getPath();String content = new String(data.getData(), StandardCharsets.UTF_8);Set<ApiDefinition> definitions = GatewayApiDefinitionManager.getApiDefinitions();List<ApiSiriusDefinition> list = JSONArray.parseArray(content, ApiSiriusDefinition.class);for (ApiSiriusDefinition apiGroup : list) {ApiDefinition api = new ApiDefinition(apiGroup.getResource()).setPredicateItems(new HashSet<ApiPredicateItem>() {{add(new ApiPathPredicateItem().setPattern(apiGroup.getUrlPath()).setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));}});definitions.add(api);}GatewayApiDefinitionManager.loadApiDefinitions(definitions);redisTemplate.opsForHash().put(API_INFO_REDIS_PREFIX + gatewayServiceProperties.getGatewayId(), path, JSONArray.toJSONString(list));log.info("<<<<<<<<<<上报api分组到sentinel成功>>>>>>>>>>");}}
}
5、sentinel控制台启动
java -Dserver.port=8600 -Dcsp.sentinel.dashboard.server=localhost:8600 -Dproject.name=sentinel-dashboard -Xms512m -Xmx512m -Xmn256m -XX:MaxMetaspaceSize=100m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/oom/log -Dfile.encoding=UTF-8 -XX:+UseG1GC -jar sentinel-dashboard-1.8.6.jar
打开sentinel控制台,请求几次接口后

可以看到相应的api分组信息和url路径匹配都已加载,在进行流量治理的时候就可以支持restful接口和controller粒度的治理了
相关文章:
spring cloud gateway集成sentinel并扩展支持restful api进行url粒度的流量治理
sentinel集成网关支持restful接口进行url粒度的流量治理前言使用网关进行总体流量治理(sentinel版本:1.8.6)1、cloud gateway添加依赖:2、sentinel配置3、网关类型项目配置4、通过zk事件监听刷新上报api分组信息1、非网关项目上报api分组信息…...
wafw00f工具
wafw00f Web应用程序防火墙指纹识别工具 github地址:https://github.com/EnableSecurity/wafw00f 安装环境:python3环境 —>使用 pip install wafw00f 进行安装 安装成功后目录:python安装目录中的Lib\site-packages\wafw00f 本机为&a…...
论文阅读笔记-DiffusionInst: Diffusion Model for Instance Segmentation
文章目录DiffusionInst: Diffusion Model for Instance Segmentation摘要介绍任务介绍实例分割的几种方法想法来源贡献方法整体结构Mask RepresentationDiffusionInst组成TrainingInference不足之处感悟DiffusionInst: Diffusion Model for Instance Segmentation 代码&#x…...
解决CondaUpgradeError网上的方法都不奏效(回退版本、upgrade/update都不行)的问题和CondaValueError
问题描述 Executing transaction: failed ERROR conda.core.link:_execute(502): An error occurred while installing package ‘conda-forge::certifi-2022.9.24-pyhd8ed1ab_0’. CondaUpgradeError: This environment has previously been operated on by a conda version…...
基于某业务单登陆场景并发测试实战
文章目录1 测试目的2 测试目标和测试对象3 名词解释4 测试说明5 测试环境和工具5.1 测试工具5.2 测试环境5.3 人力计划6 测试用例6.1 方案设计6.2 接口地址6.3 接口参数6.3.1 header参数6.3.2 请求参数7 脚本设计8 监控数据8.1 虚拟用户并发情况8.2 事务响应时间8.3 每秒点击次…...
JVM内存模型
程序计数器 多线程时,当线程数超过CPU数量或CPU内核数量,线程之间就要根据时间片轮询抢夺CPU时间资源。因此每个线程有要有一个独立的程序计数器,记录下一条要运行的指令。线程私有的内存区域。如果执行的是JAVA方法,计数器记录正…...
三、NetworkX工具包实战3——特征工程【CS224W】(Datawhale组队学习)
开源内容:https://github.com/TommyZihao/zihao_course/tree/main/CS224W 子豪兄B 站视频:https://space.bilibili.com/1900783/channel/collectiondetail?sid915098 斯坦福官方课程主页:https://web.stanford.edu/class/cs224w NetworkX…...
分布式之Raft共识算法分析
写在前面 在分布式之Paxos共识算法分析 一文中我们分析了paxos算法,知道了其包括basic paxos和multi paxos,并了解了multi paxos只是一种分布式共识算法的思想,而非具体算法,但可根据其设计具体的算法,本文就一起来看…...
数据库——范式
目录 一、概念 二、各范式 第一范式 第二范式 第三范式 BC范式 第四范式 第五范式(略) 一、概念 基本概念 关系:通常一个关系对应一张表;元组:一行;属性:一列;码࿱…...
Geospatial Data Science(2):Geospatial Data in Python
Geospatial Data Science(2):Geospatial Data in Python PART 1: 检查数据 1.1 Imports import geopandas as gpd # for geospatial data handling import osmnx # for handling data from OpenStreetMap (osm) with the help of networkX (nx) import contextily as cx # f…...
16.hadoop系列之MapReduce之MapTask与ReduceTask及Shuffle工作机制
1.MapTask工作机制 以上内容我们之前文章或多或少介绍过,就已网络上比较流行的该图进行理解学习吧 MapTask分为五大阶段 Read阶段Map阶段Collect阶段溢写阶段Merge阶段 2.ReduceTask工作机制 ReduceTask分为三大阶段 Copy阶段Sort阶段Reduce阶段 3.ReduceTask并…...
java 面试过程中遇到的几个问题记录20230220
微服务注册中心的作用微服务注册中心的作用是协调和管理微服务实例的注册和发现。它充当了服务注册表,可以维护服务实例的元数据,例如服务名称、IP 地址和端口号等。当一个微服务启动时,它会向注册中心注册自己的元数据,以使其他服…...
面试题:【数据库三】索引简述
目录 一、索引是什么 二、索引规则 三、索引失效场景 一、索引是什么 索引是帮助Mysql高效获取数据的【数据结构】索引存储在文件系统中索引的文件存储形式与存储引擎相关 mysql有三种存储引擎 InnoDBMyISAMMEMORY索引文件的结构 Hash Hash索引底层是哈希表,哈希…...
数据库必知必会:TiDB(12)TiDB连接管理
数据库必知必会:TiDB(12)TiDB连接管理TiDB连接管理TiDB的连接特性连接TiDBMySQL命令行客户端图形界面客户端连接其他连接方式写在后面TiDB连接管理 TiDB的连接特性 TiDB Server主要负责接收用户的会话请求,接收SQL并负责SQL语句…...
电源大事,阻抗二字
作者:一博科技高速先生成员 姜杰PCB设计时,我们通常会控制走线的特征阻抗;电源设计时,又会关注电源分配系统(PDN)的交流阻抗,虽然都是阻抗,一个是信号的通道要求,一个是电…...
ASE20N60-ASEMI的MOS管ASE20N60
编辑-Z ASE20N60在TO-247封装里的静态漏极源导通电阻(RDS(ON))为0.4Ω,是一款N沟道高压MOS管。ASE20N60的最大脉冲正向电流ISM为80A,零栅极电压漏极电流(IDSS)为10uA,其工作时耐温度范围为-55~150摄氏度。ASE20N60功耗…...
nginx 代理01(持续更新)
1、如果请求是post,而且请求原是188.188.3.171,处理方式403 if ($request_method ~* "POST") # $request_method 等同于request的method,通常是“GET”或“POST” # 如果访问request的method值为POST则返回“o” {set…...
初阶C语言——操作符【详解】
文章目录1.算术操作符2.移位操作符2.1 左移操作符2.2 右移操作符3.位操作符按位与按位或按位异或4.赋值操作符复合赋值符5.单目操作符5.1单目操作符介绍6.关系操作符7.逻辑操作符8.条件操作符9.逗号表达式10.下标引用、函数调用和结构成员11表达式求值11.1 隐式类型转换11.2算术…...
37k*16 薪,年后直接上岗,3年自动化测试历经3轮面试成功拿下阿里Offer....
前言 转眼过去,距离读书的时候已经这么久了吗?,从18年5月本科毕业入职了一家小公司,到现在快4年了,前段时间社招想着找一个新的工作,前前后后花了一个多月的时间复习以及面试,前几天拿到了阿里…...
利用Rust与Flutter开发一款小工具
1.起因 起因是年前看到了一篇Rust iOS & Android|未入门也能用来造轮子?的文章,作者使用Rust做了个实时查看埋点的工具。其中作者的一段话给了我启发: 无论是 LookinServer 、 Flipper 等 Debug 利器,还是 Flutt…...
接口测试中缓存处理策略
在接口测试中,缓存处理策略是一个关键环节,直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性,避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明: 一、缓存处理的核…...
<6>-MySQL表的增删查改
目录 一,create(创建表) 二,retrieve(查询表) 1,select列 2,where条件 三,update(更新表) 四,delete(删除表…...
理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端
🌟 什么是 MCP? 模型控制协议 (MCP) 是一种创新的协议,旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议,它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...
定时器任务——若依源码分析
分析util包下面的工具类schedule utils: ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类,封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz,先构建任务的 JobD…...
c#开发AI模型对话
AI模型 前面已经介绍了一般AI模型本地部署,直接调用现成的模型数据。这里主要讲述讲接口集成到我们自己的程序中使用方式。 微软提供了ML.NET来开发和使用AI模型,但是目前国内可能使用不多,至少实践例子很少看见。开发训练模型就不介绍了&am…...
深入解析C++中的extern关键字:跨文件共享变量与函数的终极指南
🚀 C extern 关键字深度解析:跨文件编程的终极指南 📅 更新时间:2025年6月5日 🏷️ 标签:C | extern关键字 | 多文件编程 | 链接与声明 | 现代C 文章目录 前言🔥一、extern 是什么?&…...
重启Eureka集群中的节点,对已经注册的服务有什么影响
先看答案,如果正确地操作,重启Eureka集群中的节点,对已经注册的服务影响非常小,甚至可以做到无感知。 但如果操作不当,可能会引发短暂的服务发现问题。 下面我们从Eureka的核心工作原理来详细分析这个问题。 Eureka的…...
LLMs 系列实操科普(1)
写在前面: 本期内容我们继续 Andrej Karpathy 的《How I use LLMs》讲座内容,原视频时长 ~130 分钟,以实操演示主流的一些 LLMs 的使用,由于涉及到实操,实际上并不适合以文字整理,但还是决定尽量整理一份笔…...
免费数学几何作图web平台
光锐软件免费数学工具,maths,数学制图,数学作图,几何作图,几何,AR开发,AR教育,增强现实,软件公司,XR,MR,VR,虚拟仿真,虚拟现实,混合现实,教育科技产品,职业模拟培训,高保真VR场景,结构互动课件,元宇宙http://xaglare.c…...
比较数据迁移后MySQL数据库和OceanBase数据仓库中的表
设计一个MySQL数据库和OceanBase数据仓库的表数据比较的详细程序流程,两张表是相同的结构,都有整型主键id字段,需要每次从数据库分批取得2000条数据,用于比较,比较操作的同时可以再取2000条数据,等上一次比较完成之后,开始比较,直到比较完所有的数据。比较操作需要比较…...
