## sentinel规则持久化
生产环境上,一般都是通过动态规则持久化的方式来动态的管理限流规则,也就是说,很多时候流控规则都会存储在文件,数据库或者配置中心当中,Sentinel的DataSource接口给我们提供了对接任意配置源的能力。所谓的动态规则就是通过控制台配置规则后持久化到各个数据源中。
![](https://img.kancloud.cn/cf/e8/cfe8227514224d3140b4e4b19cfa24c3_1130x584.png)
## sentinel的两个DataSource接口
* ReadableDataSource:读数据源负责监听持久化的数据源的变更,在接收到变更事件时将最新的数据更新
* WritableDataSource:写数据源负责将变更后的规则写入到持久化的数据源中
## sentinel-extension模块分析
在 1.7.1 版本,sentinel-extension 模块下的子模块除 sentinel-parameter-flow-control、sentinel-annotation-aspectj 之外,其余子模块都是实现动态数据源的模块。
* sentinel-datasource-extension:定义动态数据源接口、提供抽象类
* sentinel-datasource-redis:基于 Redis 实现的动态数据源
* sentinel-datasource-zookeeper: 基于 ZooKeeper 实现的动态数据源
* 其它省略
sentinel-datasource-extension 模块是 Sentinel 实现动态数据源的核心。
### SentinelProperty
SentinelProperty 是 Sentinel 提供的一个接口,可注册到 Sentinel 提供的各种规则的 Manager,例如 FlowRuleManager,并且可以给 SentinelProperty 添加监听器,在配置改变时,你可以调用 SentinelProperty#updateValue 方法,由它负责调用监听器去更新规则,而不需要调用 FlowRuleManager#loadRules 方法。同时,你也可以注册额外的监听器,在配置改变时做些别的事情。
SentinelProperty 并非 sentinel-datasource-extension 模块中定义的接口,而是 sentinel-core 定义的接口,其源码如下:
~~~
public interface SentinelProperty<T> {
void addListener(PropertyListener<T> listener);
void removeListener(PropertyListener<T> listener);
boolean updateValue(T newValue);
}
~~~
* addListener:添加监听器
* removeListener:移除监听器
* updateValue:通知所有监听器配置更新,参数 newValue 为新的配置
默认使用的实现类是 DynamicSentinelProperty,其实现源码如下:
~~~
public class DynamicSentinelProperty<T> implements SentinelProperty<T> {
// 存储注册的监听器
protected Set<PropertyListener<T>> listeners = Collections.synchronizedSet(new HashSet<PropertyListener<T>>());
@Override
public void addListener(PropertyListener<T> listener) {
listeners.add(listener);
listener.configLoad(value);
}
@Override
public void removeListener(PropertyListener<T> listener) {
listeners.remove(listener);
}
@Override
public boolean updateValue(T newValue) {
for (PropertyListener<T> listener : listeners) {
listener.configUpdate(newValue);
}
return true;
}
}
~~~
DynamicSentinelProperty 使用 Set 存储已注册的监听器,updateValue 负责通知所有监听器,调用监听器的 configUpdate 方法。
~~~
private static SentinelProperty<List<FlowRule>> currentProperty = new DynamicSentinelProperty<List<FlowRule>>();/** * Load {@link FlowRule}s, former rules will be replaced. * * @param rules new rules to load. */public static void loadRules(List<FlowRule> rules) { currentProperty.updateValue(rules);}
~~~
在前面分析 FlowRuleManager 时,我们只关注了其 loadRules 方法,除了使用 loadRules 方法加载规则配置之外,FlowRuleManager 还提供 registerProperty API,用于注册 SentinelProperty。
使用 SentinelProperty 实现加载 FlowRule 的步骤如下:
1. 给 FlowRuleManager 注册一个 SentinelProperty,替换 FlowRuleManager 默认创建的 SentinelProperty(因为默认的 SentinelProperty 外部拿不到);
2. 这一步由 FlowRuleManager 完成,FlowRuleManager 会给 SentinelProperty 注册 FlowPropertyListener 监听器,该监听器负责更新 FlowRuleManager.flowRules 缓存的限流规则;
3. 在应用启动或者规则配置改变时,只需要调用 SentinelProperty#updateValue 方法,由 updateValue 通知 FlowPropertyListener 监听器去更新规则。
FlowRuleManager 支持使用 SentinelProperty 加载或更新限流规则的实现源码如下:
~~~
public class FlowRuleManager {
// 缓存限流规则
private static final Map<String, List<FlowRule>> flowRules = new ConcurrentHashMap<String, List<FlowRule>>();
// PropertyListener 监听器
private static final FlowPropertyListener LISTENER = new FlowPropertyListener();
// SentinelProperty
private static SentinelProperty<List<FlowRule>> currentProperty
// 提供默认的 SentinelProperty
= new DynamicSentinelProperty<List<FlowRule>>();
static {
// 给默认的 SentinelProperty 注册监听器(FlowPropertyListener)
currentProperty.addListener(LISTENER);
}
// 注册 SentinelProperty
public static void register2Property(SentinelProperty<List<FlowRule>> property) {
synchronized (LISTENER) {
currentProperty.removeListener(LISTENER);
// 注册监听器
property.addListener(LISTENER);
currentProperty = property;
}
}
}
~~~
实现更新限流规则缓存的 FlowPropertyListener 是 FlowRuleManager 的一个内部类,其源码如下:
~~~
private static final class FlowPropertyListener implements PropertyListener<List<FlowRule>> {
@Override
public void configUpdate(List<FlowRule> value) {
Map<String, List<FlowRule>> rules = FlowRuleUtil.buildFlowRuleMap(value);
if (rules != null) {
// 先清空缓存再写入
flowRules.clear();
flowRules.putAll(rules);
}
}
@Override
public void configLoad(List<FlowRule> conf) {
Map<String, List<FlowRule>> rules = FlowRuleUtil.buildFlowRuleMap(conf);
if (rules != null) {
flowRules.clear();
flowRules.putAll(rules);
}
}
}
~~~
PropertyListener 接口定义的两个方法:
* configUpdate:在规则更新时被调用,被调用的时机就是在 SentinelProperty#updateValue 方法被调用时。
* configLoad:在规则首次加载时被调用,是否会被调用由 SentinelProperty 决定。DynamicSentinelProperty 就没有调用这个方法。
所以,现在我们有两种方法更新限流规则:
* 调用 FlowRuleManager#loadRules 方法
* 注册 SentinelProperty,调用 SentinelProperty#updateValue 方法
### ReadableDataSource
Sentinel 将读和写数据源抽离成两个接口,一开始只有读接口,写接口是后面才加的功能,目前来看,写接口只在热点参数限流模块中使用到。事实上,使用读接口就已经满足我们的需求。ReadableDataSource 接口的定义如下:
~~~
public interface ReadableDataSource<S, T> {
T loadConfig() throws Exception;
S readSource() throws Exception;
SentinelProperty<T> getProperty();
void close() throws Exception;
}
~~~
ReadableDataSource 是一个泛型接口,参数化类型 S 代表用于装载从数据源读取的配置的类型,参数化类型 T 代表对应 Sentinel 中的规则类型。例如,我们可以定义一个 FlowRuleProps 类,用于装载从 yml 配置文件中读取的限流规则配置,然后再将 FlowRuleProps 转为 FlowRule,所以 S 可以替换为 FlowRuleProps,T 可以替换为`List<FlowRule>`。
ReadableDataSource 接口定义的方法解释说明如下:
* loadConfig:加载配置。
* readSource:从数据源读取配置,数据源可以是 yaml 配置文件,可以是 MySQL、Redis 等,由实现类决定从哪种数据源读取配置。
* getProperty:获取 SentinelProperty。
* close:用于关闭数据源,例如使用文件存储配置时,可在此方法实现关闭文件输入流等。
如果动态数据源提供 SentinelProperty,则可以调用 getProperty 方法获取动态数据源的 SentinelProperty,将 SentinelProperty 注册给规则管理器(XxxManager),动态数据源在读取到配置时就可以调用自身 SentinelProperty 的 updateValue 方法通知规则管理器(XxxManager)更新规则。
AbstractDataSource 是一个抽象类,该类实现 ReadableDataSource 接口,用于简化具体动态数据源的实现,子类只需要继承 AbstractDataSource 并实现 readSource 方法即可。AbstractDataSource 源码如下:
~~~
public abstract class AbstractDataSource<S, T> implements ReadableDataSource<S, T> {
protected final Converter<S, T> parser;
protected final SentinelProperty<T> property;
public AbstractDataSource(Converter<S, T> parser) {
if (parser == null) {
throw new IllegalArgumentException("parser can't be null");
}
this.parser = parser;
this.property = new DynamicSentinelProperty<T>();
}
@Override
public T loadConfig() throws Exception {
return loadConfig(readSource());
}
public T loadConfig(S conf) throws Exception {
T value = parser.convert(conf);
return value;
}
@Override
public SentinelProperty<T> getProperty() {
return property;
}
}
~~~
从源码可以看出:
* AbstractDataSource 要求所有子类都必须提供一个数据转换器(Converter),Converter 用于将 S 类型的对象转为 T 类型对象,例如将 FlowRuleProps 对象转为 FlowRule 集合。
* AbstractDataSource 在构造方法中创建 DynamicSentinelProperty,因此子类无需创建 SentinelProperty。
* AbstractDataSource 实现 loadConfig 方法,首先调用子类实现的 readSource 方法从数据源读取配置,返回的对象类型为 S,再调用 Converter#convert 方法,将对象类型由 S 转为 T。
Converter 接口的定义如下:
~~~
public interface Converter<S, T> {
T convert(S source);
}
~~~
* convert:将类型为 S 的参数 source 转为类型为 T 的对象。
## sentinel可以支持4种持久化策略
* nacos分布式配置中心
* 阿波罗配置中心
* zookeeper
* 本地文件
![](https://img.kancloud.cn/5a/a1/5aa17a1415deab4ffea710f632e09662_912x138.png)
## 规则流程
![](https://img.kancloud.cn/b0/7b/b07ba907e33149af08aed8ee1495cf63_1058x503.png)
## 改造内容
### 推模式
![](https://img.kancloud.cn/25/2e/252e4902ee18969389a8abb89ed86f8a_991x590.png)
* 增加nacos地址
![](https://img.kancloud.cn/55/9a/559a8ff834f6cb2e74d48acf061621fa_1471x650.png)
* 增加配置
![](https://img.kancloud.cn/41/65/416537d31f9c4163c31b9acab45561a0_1531x579.png)
* 增加nacos
![](https://img.kancloud.cn/a8/28/a828349d68f4cb5cb0326306690260e8_506x661.png)
* 修改 com.alibaba.csp.sentinel.dashboard.controller.v2.FlowControllerV2 ,找到
![](https://img.kancloud.cn/89/38/89385021f6432d748586bf4973d6157f_1757x679.png)
* 注释选中代码
![](https://img.kancloud.cn/16/1d/161d7a99a15b8627ba9ff4dc4c23aa7e_507x571.png)
* 修改流控规则代码
![](https://img.kancloud.cn/41/9a/419a495682a3aa1c3ea8b36dcc66a6d8_1661x497.png)
- 前言
- 1.项目说明
- 2.项目更新日志
- 3.文档更新日志
- 01.快速开始
- 01.maven构建项目
- 02.环境安装
- 03.STS项目导入
- 03.IDEA项目导入
- 04.数据初始化
- 05.项目启动
- 06.付费文档说明
- 02.总体流程
- 1.oauth接口
- 2.架构设计图
- 3.微服务介绍
- 4.功能介绍
- 5.梳理流程
- 03.模块详解
- 01.老版本1.0.1分支模块讲解
- 01.db-core模块
- 02.api-commons模块
- 03.log-core模块
- 04.security-core模块
- 05.swagger-core模块
- 06.eureka-server模块
- 07.auth-server模块
- 08.auth-sso模块解析
- 09.user-center模块
- 10.api-gateway模块
- 11.file-center模块
- 12.log-center模块
- 13.batch-center模块
- 14.back-center模块
- 02.spring-boot-starter-web那点事
- 03.自定义db-spring-boot-starter
- 04.自定义log-spring-boot-starter
- 05.自定义redis-spring-boot-starter
- 06.自定义common-spring-boot-starter
- 07.自定义swagger-spring-boot-starter
- 08.自定义uaa-server-spring-boot-starter
- 09.自定义uaa-client-spring-boot-starter
- 10.自定义ribbon-spring-boot-starter
- 11.springboot启动原理
- 12.eureka-server模块
- 13.auth-server模块
- 14.user-center模块
- 15.api-gateway模块
- 16.file-center模块
- 17.log-center模块
- 18.back-center模块
- 19.auth-sso模块
- 20.admin-server模块
- 21.zipkin-center模块
- 22.job-center模块
- 23.batch-center
- 04.全新网关
- 01.基于spring cloud gateway的new-api-gateway
- 02.spring cloud gateway整合Spring Security Oauth
- 03.基于spring cloud gateway的redis动态路由
- 04.spring cloud gateway聚合swagger文档
- 05.技术详解
- 01.互联网系统设计原则
- 02.系统幂等性设计与实践
- 03.Oauth最简向导开发指南
- 04.oauth jdbc持久化策略
- 05.JWT token方式启用
- 06.token有效期的处理
- 07.@PreAuthorize注解分析
- 08.获取当前用户信息
- 09.认证授权白名单配置
- 10.OCP权限设计
- 11.服务安全流程
- 12.认证授权详解
- 13.验证码技术
- 14.短信验证码登录
- 15.动态数据源配置
- 16.分页插件使用
- 17.缓存击穿
- 18.分布式主键生成策略
- 19.分布式定时任务
- 20.分布式锁
- 21.网关多维度限流
- 22.跨域处理
- 23.容错限流
- 24.应用访问次数控制
- 25.统一业务异常处理
- 26.日志埋点
- 27.GPRC内部通信
- 28.服务间调用
- 29.ribbon负载均衡
- 30.微服务分布式跟踪
- 31.异步与线程传递变量
- 32.死信队列延时消息
- 33.单元测试用例
- 34.Greenwich.RELEASE升级
- 35.混沌工程质量保证
- 06.开发初探
- 1.开发技巧
- 2.crud例子
- 3.新建服务
- 4.区分前后台用户
- 07.分表分库
- 08.分布式事务
- 1.Seata介绍
- 2.Seata部署
- 09.shell部署
- 01.eureka-server
- 02.user-center
- 03.auth-server
- 04.api-gateway
- 05.file-center
- 06.log-center
- 07.back-center
- 08.编写shell脚本
- 09.集群shell部署
- 10.集群shell启动
- 11.部署阿里云问题
- 10.网关安全
- 1.openresty https保障服务安全
- 2.openresty WAF应用防火墙
- 3.openresty 高可用
- 11.docker配置
- 01.docker安装
- 02.Docker 开启远程API
- 03.采用docker方式打包到服务器
- 04.docker创建mysql
- 05.docker网络原理
- 06.docker实战
- 6.01.安装docker
- 6.02.管理镜像基本命令
- 6.03.容器管理
- 6.04容器数据持久化
- 6.05网络模式
- 6.06.Dockerfile
- 6.07.harbor部署
- 6.08.使用自定义镜像
- 12.统一监控中心
- 01.spring boot admin监控
- 02.Arthas诊断利器
- 03.nginx监控(filebeat+es+grafana)
- 04.Prometheus监控
- 05.redis监控(redis+prometheus+grafana)
- 06.mysql监控(mysqld_exporter+prometheus+grafana)
- 07.elasticsearch监控(elasticsearch-exporter+prometheus+grafana)
- 08.linux监控(node_exporter+prometheus+grafana)
- 09.micoservice监控
- 10.nacos监控
- 11.druid数据源监控
- 12.prometheus.yml
- 13.grafana告警
- 14.Alertmanager告警
- 15.监控微信告警
- 16.关于接口监控告警
- 17.prometheus-HA架构
- 18.总结
- 13.统一日志中心
- 01.统一日志中心建设意义
- 02.通过ELK收集mysql慢查询日志
- 03.通过elk收集微服务模块日志
- 04.通过elk收集nginx日志
- 05.统一日志中心性能优化
- 06.kibana安装部署
- 07.日志清理方案
- 08.日志性能测试指标
- 09.总结
- 14.数据查询平台
- 01.数据查询平台架构
- 02.mysql配置bin-log
- 03.单节点canal-server
- 04.canal-ha部署
- 05.canal-kafka部署
- 06.实时增量数据同步mysql
- 07.canal监控
- 08.clickhouse运维常见脚本
- 15.APM监控
- 1.Elastic APM
- 2.Skywalking
- 01.docker部署es
- 02.部署skywalking-server
- 03.部署skywalking-agent
- 16.压力测试
- 1.ocp.jmx
- 2.test.bat
- 3.压测脚本
- 4.压力报告
- 5.报告分析
- 6.压测平台
- 7.并发测试
- 8.wrk工具
- 9.nmon
- 10.jmh测试
- 17.SQL优化
- 1.oracle篇
- 01.基线测试
- 02.调优前奏
- 03.线上瓶颈定位
- 04.执行计划解读
- 05.高级SQL语句
- 06.SQL tuning
- 07.数据恢复
- 08.深入10053事件
- 09.深入10046事件
- 2.mysql篇
- 01.innodb存储引擎
- 02.BTree索引
- 03.执行计划
- 04.查询优化案例分析
- 05.为什么会走错索引
- 06.表连接优化问题
- 07.Connection连接参数
- 08.Centos7系统参数调优
- 09.mysql监控
- 10.高级SQL语句
- 11.常用维护脚本
- 12.percona-toolkit
- 18.redis高可用方案
- 1.免密登录
- 2.安装部署
- 3.配置文件
- 4.启动脚本
- 19.消息中间件搭建
- 19-01.rabbitmq集群搭建
- 01.rabbitmq01
- 02.rabbitmq02
- 03.rabbitmq03
- 04.镜像队列
- 05.haproxy搭建
- 06.keepalived
- 19-02.rocketmq搭建
- 19-03.kafka集群
- 20.mysql高可用方案
- 1.环境
- 2.mysql部署
- 3.Xtrabackup部署
- 4.Galera部署
- 5.galera for mysql 集群
- 6.haproxy+keepalived部署
- 21.es集群部署
- 22.生产实施优化
- 1.linux优化
- 2.jvm优化
- 3.feign优化
- 4.zuul性能优化
- 23.线上问题诊断
- 01.CPU性能评估工具
- 02.内存性能评估工具
- 03.IO性能评估工具
- 04.网络问题工具
- 05.综合诊断评估工具
- 06.案例诊断01
- 07.案例诊断02
- 08.案例诊断03
- 09.案例诊断04
- 10.远程debug
- 24.fiddler抓包实战
- 01.fiddler介绍
- 02.web端抓包
- 03.app抓包
- 25.疑难解答交流
- 01.有了auth/token获取token了为啥还要配置security的登录配置
- 02.权限数据存放在redis吗,代码在哪里啊
- 03.其他微服务和认证中心的关系
- 04.改包问题
- 05.use RequestContextListener or RequestContextFilter to expose the current request
- 06./oauth/token对应代码在哪里
- 07.验证码出不来
- 08./user/login
- 09.oauth无法自定义权限表达式
- 10.sleuth引发线程数过高问题
- 11.elk中使用7x版本问题
- 12.RedisCommandTimeoutException问题
- 13./oauth/token CPU过高
- 14.feign与权限标识符问题
- 15.动态路由RedisCommandInterruptedException: Command interrupted
- 26.学习资料
- 海量学习资料等你来拿
- 27.持续集成
- 01.git安装
- 02.代码仓库gitlab
- 03.代码仓库gogs
- 04.jdk&&maven
- 05.nexus安装
- 06.sonarqube
- 07.jenkins
- 28.Rancher部署
- 1.rancher-agent部署
- 2.rancher-server部署
- 3.ocp后端部署
- 4.演示前端部署
- 5.elk部署
- 6.docker私服搭建
- 7.rancher-server私服
- 8.rancher-agent docker私服
- 29.K8S部署OCP
- 01.准备OCP的构建环境和部署环境
- 02.部署顺序
- 03.在K8S上部署eureka-server
- 04.在K8S上部署mysql
- 05.在K8S上部署redis
- 06.在K8S上部署auth-server
- 07.在K8S上部署user-center
- 08.在K8S上部署api-gateway
- 09.在K8S上部署back-center
- 30.Spring Cloud Alibaba
- 01.统一的依赖管理
- 02.nacos-server
- 03.生产可用的Nacos集群
- 04.nacos配置中心
- 05.common.yaml
- 06.user-center
- 07.auth-server
- 08.api-gateway
- 09.log-center
- 10.file-center
- 11.back-center
- 12.sentinel-dashboard
- 12.01.sentinel流控规则
- 12.02.sentinel熔断降级规则
- 12.03.sentinel热点规则
- 12.04.sentinel系统规则
- 12.05.sentinel规则持久化
- 12.06.sentinel总结
- 13.sentinel整合openfeign
- 14.sentinel整合网关
- 1.sentinel整合zuul
- 2.sentinel整合scg
- 15.Dubbo与Nacos共存
- 31.Java源码剖析
- 01.基础数据类型和String
- 02.Arrays工具类
- 03.ArrayList源码分析
- 32.面试专题汇总
- 01.JVM专题汇总
- 02.多线程专题汇总
- 03.Spring专题汇总
- 04.springboot专题汇总
- 05.springcloud面试汇总
- 文档问题跟踪处理