【深入理解SpringCloud微服务】Sentinel规则持久化实战
Sentinel规则持久化实战
- Sentinel规则推送模式
- 原始模式
- pull(拉模式)
- push(推模式)
- 实现拉模式
- 实现推模式
Sentinel规则推送模式
原始模式
原始模式由Sentinel控制台直接推送规则到Sentinel客户端,Sentinel客户端将规则保存到内存中。这种模式是最简单的,不需要做任何改造,但是一旦Sentinel客户端重启,所有的规则都会丢失。

pull(拉模式)
拉模式由Sentinel客户端主动轮询规则管理中心,更新规则到内存中。这个规则管理中心可以是关系型数据库或者文件,因此这种模式已具备了规则持久化的能力,但是实时性无法保证(因为是定时轮询的)。
拉模式要扩展并注册写数据源WritableDataSource和读数据源ReadableDataSource。当Sentinel客户端接收到Sentinel控制台推送的规则时,将规则更新到内存的同时,会通过WritableDataSource将规则持久化到规则管理中心。而ReadableDataSource则负责定时轮询规则管理中心,当规则管理中心发生规则变更时,则更新到内存。

push(推模式)
推模式由规则管理中心主动将变更的规则推送给Sentinel客户端,Sentinel客户端通过注册监听器的方式监听规则管理中心推送的规则变更通知。此时的规则管理中心就不能是关系型数据库或文件了,而是nacos或zookeeper等具备主动通知能力的配置中心。
推模式需要扩展并注册ReadableDataSource,ReadableDataSource通过监听器实时监听配置中心推送的规则变更通知,更新到内存。

此时有一个问题,在Sentinel控制台上更新的规则,在配置中心没有变化。
解决这个问题有两个办法。
第一种办法是:Sentinel客户端注册一个WritableDataSource,当Sentinel客户端收到Sentinel控制台推送的新增或更新的规则时,通过WritableDataSource发布规则到配置中心。

第二种办法是修改Sentinel控制台的源码,Sentinel控制台不和Sentinel客户端直接通讯,而是把在控制台上新增或修改的规则保存到配置中心,由配置中心通知Sentinel客户端。同时Sentinel控制台也不再通过Sentinel客户端提供的API拉取规则,而是从配置中心读取。

实现拉模式
引入Maven依赖(如果引入的是spring-cloud-starter-alibaba-sentinel则不需要,里面已经包含了):
<dependency><groupId>com.alibaba.csp</groupId><artifactId>sentinel-datasource-extension</artifactId><version>1.8.5</version></dependency>
通过实现InitFunc接口,并重新init方法进行读数据源和写数据源的注册,Sentinel内部会通过SPI机制加载InitFunc的实现类并执行init方法。
/** * 实现InitFunc接口,Sentinel内部会通过SPI机制加载* @author huangjunyi * @date 2024年4月21日 下午1:48:09* @desc */
public class FlowRuleFileDataSourceInit implements InitFunc {@Overridepublic void init() throws Exception {// 定位本地流控规则配置文件路径ClassLoader classLoader = getClass().getClassLoader();String flowRulePath = URLDecoder.decode(classLoader.getResource("FlowRule.json").getFile(), "UTF-8");System.out.println("flowRulePath: " + flowRulePath);// 注解读数据源到RuleManager FileRefreshableDataSource<List<FlowRule>> flowRuleDataSource = new FileRefreshableDataSource<>(flowRulePath, source -> JSON.parseObject(source,new TypeReference<List<FlowRule>>() {}));FlowRuleManager.register2Property(flowRuleDataSource.getProperty());// 注册写数据源到通讯(transport)模块的WritableDataSourceRegistryWritableDataSource<List<FlowRule>> wds = new FileWritableDataSource<>(flowRulePath, t -> JSON.toJSONString(t));WritableDataSourceRegistry.registerFlowDataSource(wds);}}
/resources/META-INF/services/com.alibaba.csp.sentinel.init.InitFunc:
com.huangjunyi1993.config.FlowRuleFileDataSourceInit
准备一个本地的流控规则配置文件
/resources/FlowRule.json:
[{"resource": "/my","controlBehavior": 0,"count": 2,"grade": 1,"limitApp": "default","strategy": 0}
]
这样就完成了。

此时的拉模式是通过定时轮询本地配置文件实现的:

我们在Sentinel控制台上配置的规则,就会持久化到文件中。比如我们新增一条流控规则:

在文件中就可以看到持久化的规则:

修改文件的规则:

控制台也会发生变化:

调用接口,验证流控规则是否生效:

实现推模式
引入依赖:
<dependency><groupId>com.alibaba.csp</groupId><artifactId>sentinel-datasource-nacos</artifactId><version>1.8.5</version></dependency>
这个在Sentinel的源码包的demo模块中也有示例,我们拉过来直接改一改:
public class NacosDataSourceInit implements InitFunc {// nacos server ipprivate static final String remoteAddress = "localhost:8848";// nacos groupprivate static final String groupId = "Sentinel_Demo";// nacos dataIdprivate static final String dataId = "com.alibaba.csp.sentinel.demo.flow.rule";private static void loadRules() {ReadableDataSource<String, List<FlowRule>> flowRuleDataSource = new NacosDataSource<>(remoteAddress, groupId, dataId,source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {}));FlowRuleManager.register2Property(flowRuleDataSource.getProperty());}@Overridepublic void init() throws Exception {loadRules();}}
/resources/META-INF/services/com.alibaba.csp.sentinel.init.InitFunc:
com.huangjunyi1993.config.NacosDataSourceInit
在nacos上创建配置文件:

启动应用并验证:


这种是硬编码的配置模式,其实还有更简单的。如果我们引入的是spring-cloud-starter-alibaba-sentinel,那么我们只要在bootstrap.yml配置nacos地址
/resources/bootstrap.yml
server:port: 8888spring:application:name: democloud:nacos:# 配置nacos地址server-addr: 127.0.0.1:8848sentinel:transport:# 控制台地址dashboard: 127.0.0.1:8080port: 8719datasource:nacos-flow-rule: nacos:# nacos地址、dataId、groupIdserver-addr: 127.0.0.1:8848dataId: ${spring.application.name}groupId: DEFAULT_GROUPrule-type: flow data‐type: json management:endpoints:web:exposure:include: '*'
由于我们配置的dataId是我们的应用名,因此nacos上的dataId要与应用名一致:

这种方式就更加简单了。

启动应用并验证:

此时我们在Sentinel控制台上修改规则配置时,发现nacos上的配置没有发生变化。因此我们接下来解决这个问题,我们使用修改Sentinel控制台的方式实现,我们可以参考sentinel-dashboard下的/src/test目录下的例子进行改造。

NacosConfig中配置NacosConfigService,用于控制台与nacos通讯:
@Configuration
public class NacosConfig {@Beanpublic Converter<List<FlowRuleEntity>, String> flowRuleEntityEncoder() {return JSON::toJSONString;}@Beanpublic Converter<String, List<FlowRuleEntity>> flowRuleEntityDecoder() {return s -> JSON.parseArray(s, FlowRuleEntity.class);}// 配置NacosConfigService,用于控制台与nacos通讯@Beanpublic ConfigService nacosConfigService() throws Exception {return ConfigFactory.createConfigService("localhost:8848");}
}
新增FlowRuleNacosProvider,用于控制台从nacos拉取规则配置:
@Component("flowRuleNacosProvider")
public class FlowRuleNacosProvider implements DynamicRuleProvider<List<FlowRuleEntity>> {@Autowiredprivate ConfigService configService;@Autowiredprivate Converter<String, List<FlowRuleEntity>> converter;@Overridepublic List<FlowRuleEntity> getRules(String appName) throws Exception {// 通过configService从nacos拉取规则配置String rules = configService.getConfig(appName + NacosConfigUtil.FLOW_DATA_ID_POSTFIX,NacosConfigUtil.GROUP_ID, 3000);if (StringUtil.isEmpty(rules)) {return new ArrayList<>();}return converter.convert(rules);}
}
新增FlowRuleNacosPublisher,用于控制台把规则配置发布到nacos:
@Component("flowRuleNacosPublisher")
public class FlowRuleNacosPublisher implements DynamicRulePublisher<List<FlowRuleEntity>> {@Autowiredprivate ConfigService configService;@Autowiredprivate Converter<List<FlowRuleEntity>, String> converter;@Overridepublic void publish(String app, List<FlowRuleEntity> rules) throws Exception {AssertUtil.notEmpty(app, "app name cannot be empty");if (rules == null) {return;}// 发布规则配置到控制台configService.publishConfig(app + NacosConfigUtil.FLOW_DATA_ID_POSTFIX,NacosConfigUtil.GROUP_ID, converter.convert(rules));}
}
对com.alibaba.csp.sentinel.dashboard.controller.FlowControllerV1进行改造:

注释掉SentinelApiClient,然后引入FlowRuleNacosProvider和FlowRuleNacosPublisher。


原先使用SentinelApiClient拉取规则配置的地方改成使用FlowRuleNacosProvider,原先使用SentinelApiClient发布规则配置的地方改成使用FlowRuleNacosPublisher。
改造前的sentinel-dashboard是这样:

改造后的sentinel-dashboard是这样:

流控规则就改造完了,改动和添加的几个文件如下:

其余类型的规则也是参照这个套路修改即可。
然后在nacos上创建一个配置文件,按照NacosConfigUtil指定的命名规则,Data Id的格式为{app}-flow-rules,group则是SENTINEL_GROUP。



然后我们的应用程序的bootstrap.yml文件配置的dataId和groupId也要改成相应格式:
spring:...cloud:...sentinel:...datasource:nacos-flow-rule: nacos:...dataId: ${spring.application.name}-flow-rulesgroupId: SENTINEL_GROUP...
启动控制台并验证:

发现从nacos拉取到了流控规则,我们修改流程规则:

流控规则更新到控制台:

最后还有个问题,如果热点参数流程也按照这个套路来改的话,会导致其失效。原因是因为Sentinel控制台发布到nacos的格式是按照ParamFlowRuleEntity的格式推的,而Sentinel从nacos拉取到热点参数规则时,是解析成ParamFlowRule的。

一种办法是修改Sentinel客户端的converter,让它能成功的从ParamFlowRuleEntity格式的json解析成ParamFlowRule。

另一种办法是修改Sentinel控制台的converter。当发布规则到nacos时,从ParamFlowRuleEntity格式的对象转成ParamFlowRule格式的json;当从nacos拉取规则时,从ParamFlowRule格式的json转成ParamFlowRuleEntity格式的对象。

相关文章:
【深入理解SpringCloud微服务】Sentinel规则持久化实战
Sentinel规则持久化实战 Sentinel规则推送模式原始模式pull(拉模式)push(推模式) 实现拉模式实现推模式 Sentinel规则推送模式 原始模式 原始模式由Sentinel控制台直接推送规则到Sentinel客户端,Sentinel客户端将规则…...
三高“高性能、高并发、高可靠”系统架构设计系列文章
目录 高并发系统的艺术:如何在流量洪峰中游刃有余 《数据密集型应用系统设计》读后感与高并发高性能实践案例 系统稳定性与高可用保障的几种思路 软件系统限流的底层原理解析 技术解决方案调研 延迟队列调研 重试调研 异步回调调研 分库分表调研 分布式事…...
ray.rllib-入门实践-11: 自定义模型/网络
在ray.rllib中定义和使用自己的模型, 分为以下三个步骤: 1. 定义自己的模型。 2. 向ray注册自定义的模型 3. 在config中配置使用自定义的模型 环境配置: torch2.5.1 ray2.10.0 ray[rllib]2.10.0 ray[tune]2.10.0 ray[serve]2.10.0 numpy1.23.…...
C语言小项目——通讯录
功能介绍: 1.联系人信息:姓名年龄性别地址电话 2.通讯录中可以存放100个人的信息 3.功能: 1>增加联系人 2>删除指定联系人 3>查找指定联系人的信息 4>修改指定联系人的信息 5显示所有联系人的信息 6>排序(名字&…...
C#牵手Blazor,解锁跨平台Web应用开发新姿势
一、引言 在当今数字化时代,Web 应用已成为人们生活和工作中不可或缺的一部分 ,而开发跨平台的 Web 应用则是满足不同用户需求、扩大应用影响力的关键。C# 作为一种强大的编程语言,拥有丰富的类库和强大的功能,在企业级开发、游戏…...
PCIE模式配置
对于VU系列FPGA,当DMA/Bridge Subsystem for PCI Express IP配置为Bridge模式时,等同于K7系列中的AXI Memory Mapped To PCI Express IP。...
【论文阅读】RT-SKETCH: GOAL-CONDITIONED IMITATION LEARNING FROM HAND-DRAWN SKETCHES
RT-Sketch:基于手绘草图的目标条件模仿学习 摘要:在目标条件模仿学习(imitation learning,IL)中,自然语言和图像通常被用作目标表示。然而,自然语言可能存在歧义,图像则可能过于具体…...
【由浅入深认识Maven】第2部分 maven依赖管理与仓库机制
文章目录 第二篇:Maven依赖管理与仓库机制一、前言二、依赖管理基础1.依赖声明2. 依赖范围(Scope)3. 依赖冲突与排除 三、Maven的仓库机制1. 本地仓库2. 中央仓库3. 远程仓库 四、 版本管理策略1. 固定版本2. 版本范围 五、 总结 第二篇&…...
centos 安全配置基线
CentOS 是一个广泛使用的操作系统,为了确保系统的安全性,需要遵循一系列的安全基线。以下是详细的 CentOS 安全基线配置建议: 通过配置核查,CentOS操作系统未安装入侵防护软件,无法检测到对重要节点进行入侵的 解决方案: 安装入侵…...
备赛蓝桥杯之第十五届职业院校组省赛第一题:智能停车系统
提示:本篇文章仅仅是作者自己目前在备赛蓝桥杯中,自己学习与刷题的学习笔记,写的不好,欢迎大家批评与建议 由于个别题目代码量与题目量偏大,请大家自己去蓝桥杯官网【连接高校和企业 - 蓝桥云课】去寻找原题࿰…...
力扣 Hot 100 题解 (js版)更新ing
🚩哈希表 ✅ 1. 两数之和 Code: 暴力法 复杂度分析: 时间复杂度: ∗ O ( N 2 ) ∗ *O(N^2)* ∗O(N2)∗,其中 N 是数组中的元素数量。最坏情况下数组中任意两个数都要被匹配一次。空间复杂度:O(1)。 /…...
DeepSeek-R1:性能对标 OpenAI,开源助力 AI 生态发展
DeepSeek-R1:性能对标 OpenAI,开源助力 AI 生态发展 在人工智能领域,大模型的竞争一直备受关注。最近,DeepSeek 团队发布了 DeepSeek-R1 模型,并开源了模型权重,这一举动无疑为 AI 领域带来了新的活力。今…...
CY T 4 BB 5 CEB Q 1 A EE GS MCAL配置 - MCU组件
1、ResourceM 配置 选择芯片信号: 2、MCU 配置 2.1 General配置 1) McuDevErrorDetect: - 启用或禁用MCU驱动程序模块的开发错误通知功能。 - 注意:采用DET错误检测机制作为安全机制(故障检测)时,不能禁用开发错误检测。2) McuGetRamStateApi - enable/disable th…...
传输层协议TCP与UDP:深入解析与对比
传输层协议TCP与UDP:深入解析与对比 目录 传输层协议TCP与UDP:深入解析与对比引言1. 传输层协议概述2. TCP协议详解2.1 TCP的特点2.2 TCP的三次握手与四次挥手三次握手四次挥手 2.3 TCP的流量控制与拥塞控制2.4 TCP的可靠性机制 3. UDP协议详解3.1 UDP的…...
校园商铺管理系统设计与实现(代码+数据库+LW)
摘 要 信息数据从传统到当代,是一直在变革当中,突如其来的互联网让传统的信息管理看到了革命性的曙光,因为传统信息管理从时效性,还是安全性,还是可操作性等各个方面来讲,遇到了互联网时代才发现能补上自…...
【JavaWeb学习Day13】
Tlias智能学习系统 需求: 部门管理:查询、新增、修改、删除 员工管理:查询、新增、修改、删除和文件上传 报表统计 登录认证 日志管理 班级、学员管理(实战内容) 部门管理: 01准备工作 开发规范-…...
springboot使用tomcat浅析
springboot使用tomcat浅析 关于外部tomcat maven pom配置 // 打包时jar包改为war包 <packaging>war</packaging>// 内嵌的tomcat的scope标签影响范围设置为provided,只在编译和测试时有效,打包时不带入 <dependency><groupId>…...
rust 自定义错误(十二)
错误定义: let file_content parse_file("test.txt");if let Err(e) file_content {println!("Error: {:?}", e);}let file_content parse_file2("test.txt");if let Err(e) file_content {match e {ParseFileError::File > …...
如何使用CRM数据分析优化销售和客户关系?
嘿,大家好!你有没有想过为什么有些公司在市场上如鱼得水,而另一些却在苦苦挣扎?答案可能就藏在他们的销售策略和客户关系管理(CRM)系统里。今天我们要聊的就是如何通过有效的 CRM 数据分析来提升你的销售额…...
导出地图为pdf文件
有时我们只是想创建能共享的pdf文件,而不是将地图打印出来,arcpy的ExportToPDF()函数可以实现该功能. 操作方法: 1.在arcmap中打开目标地图 2.导入arcpy.mapping模块 import arcpy.mapping as mapping 3.引用当前活动地图文档,把该引用赋值给变量 mxd mapping.MapDocumen…...
Qt 控件与布局管理
1. Qt 控件的父子继承关系 在 Qt 中,继承自 QWidget 的类,通常会在构造函数中接收一个 parent 参数。 这个参数用于指定当前空间的父控件,从而建立控件间的父子关系。 当一个控件被设置为另一控件的子控件时,它会自动成为该父控…...
电力场效应晶体管(电力 MOSFET),全控型器件
电力场效应晶体管(Power MOSFET)属于全控型器件是一种电压触发的电力电子器件,一种载流子导电(单极性器件)一个器件是由一个个小的mosfet组成以下是相关介绍: 工作原理(栅极电压控制漏极电流&a…...
一文讲解Java中的重载、重写及里氏替换原则
提到重载和重写,Java小白应该都不陌生,接下来就通过这篇文章来一起回顾复习下吧! 重载和重写有什么区别呢? 如果一个类有多个名字相同但参数不同的方法,我们通常称这些方法为方法重载Overload。如果方法的功能是一样…...
StarRocks常用命令
目录 1、StarRocks 集群管理&配置命令 2、StarRocks 常用操作命令 3、StarRocks 数据导入和导出 1、StarRocks 集群管理&配置命令 查询 FE 节点信息 SHOW frontends; SHOW PROC /frontends; mysql -h192.168.1.250 -P9030 -uroot -p -e "SHOW PROC /dbs;"…...
Pandas基础02(DataFrame创建/索引/切片/属性/方法/层次化索引)
DataFrame数据结构 DataFrame 是一个二维表格的数据结构,类似于数据库中的表格或 Excel 工作表。它由多个 Series 组成,每个 Series 共享相同的索引。DataFrame 可以看作是具有列名和行索引的二维数组。设计初衷是将Series的使用场景从一维拓展到多维。…...
Meta-CoT:通过元链式思考增强大型语言模型的推理能力
大型语言模型(LLMs)在处理复杂推理任务时面临挑战,这突显了其在模拟人类认知中的不足。尽管 LLMs 擅长生成连贯文本和解决简单问题,但在需要逻辑推理、迭代方法和结果验证的复杂任务(如高级数学问题和抽象问题解决&…...
【时时三省】(C语言基础)二进制输入输出
山不在高,有仙则名。水不在深,有龙则灵。 ----CSDN 时时三省 二进制输入 用fread可以读取fwrite输入的内容 字符串以文本的形式写进去的时候,和以二进制写进去的内容是一样的 整数和浮点型以二进制写进去是不一样的 二进制输出 fwrite 字…...
【go语言】数组和切片
一、数组 1.1 什么是数组 数组是一组数:数组需要是相同类型的数据的集合;数组是需要定义大小的;数组一旦定义了大小是不可以改变的。 1.2 数组的声明 数组和其他变量定义没有什么区别,唯一的就是这个是一组数,需要给…...
10.片元
**片元(Fragment)**是渲染管线中的一个重要概念,可以理解为“潜在的像素”。用通俗易懂的方式来解释: 通俗解释:片元就像候选的颜料点 想象你是一个画家,正在画一幅画: 片元是候选的颜料点&…...
SQL-leetcode—1179. 重新格式化部门表
1179. 重新格式化部门表 表 Department: ---------------------- | Column Name | Type | ---------------------- | id | int | | revenue | int | | month | varchar | ---------------------- 在 SQL 中,(id, month) 是表的联合主键。 这个表格有关…...
