资讯动态

Sentinel降级规则推送到Nacos:实现持久化与多实例同步

发布时间:2026/10/14 23:23:48 来源:尧图企业网站定制
最近在弄一套微服务治理链路核心业务的熔断降级统一用 Sentinel。界面上新增一条降级规则很快但它真正的作用范围往往会让第一次接触的人误判Dashboard 把规则推给客户端后规则只活在客户端进程的内存里。重启一次应用规则瞬间归零。后来我把这条链路改成“Dashboard 推送降级规则直接写入 Nacos”应用侧通过 Nacos 数据源监听并加载重启不丢、多实例同步也才真正落地。这篇文章围绕这条改造链路展开梳理清楚为什么默认方式会丢规则、推模式和拉模式之间的差异并给出能从零复现的步骤。很适合正在做微服务治理、正在从本地规则迁移到配置中心的团队参考。1. 为什么需要把降级规则推送到 Nacos1.1 默认推送链路到底把规则放哪了默认情况下客户端会开启 transport 模块并监听 8719 端口Dashboard 通过客户端提供的 HTTP 接口把规则下发下去。客户端收到后规则被放进了DegradeRuleManager里的内存结构然后针对每个资源实时生效。整个流程从头到尾没有使用任何外部存储。这个机制听起来不复杂但有一个致命问题规则是进程内的状态。应用发布、扩缩容重启、机器迁移任何一次进程重启都会让规则回到零。尤其当你维护多个实例的集群一条规则要在 Dashboard 上重新点好几遍而且每个实例上的规则还不一定能保持一致。之前有一次下午发布新版本当晚线上接口开始变慢所有降级规则全部失效因为发布流程把整个服务重启了。从那之后我就不太信任纯内存的规则管理模式。1.2 配置中心在规则场景里的定位与其说这是一个规则推送问题不如说这是一个规则存放和分发问题。Nacos 在微服务体系里通常做注册中心和配置中心规则本身也是配置的一种所以把降级规则放到 Nacos 合理且顺手。放到 Nacos 之后规则从一个进程内的临时对象变成了一个带版本、可审计、可回滚的配置项。只要 Nacos 没丢应用随便重启启动时都能通过数据源拿到最新的规则。对多个应用实例而言规则只有一份在 Nacos所有实例监听同一个 dataId天然避免两边不一致。1.3 为什么最终选了推模式而不是拉模式做成这种持久化一般有几种选择我实际对比过。第一是本地文件持久化规则写成文件应用启动时读取。这种方案简单但每个实例一份文件更新时还要想办法同步集群场景下很快就乱。第二是客户端主动轮询数据库周期性地从规则表拉取并加载。这种方案介入了业务数据库且轮询间隔导致规则生效有延迟多实例之间也存在时间差。第三就是 Nacos 推模式Nacos 客户端通过长轮询和监听机制感知内容变化配置发布后能很快推给各监听者规则生效快数据也集中。模式规则存储位置生效方式主要问题内存模式客户端 JVM 内存Dashboard 直接调客户端接口重启丢规则多实例难一致本地文件拉取本地磁盘应用启动时加载不能动态变更无法统一管理数据库轮询业务数据库客户端定时轮询实现复杂生效慢Nacos 推模式Nacos 配置中心Nacos 监听推送需要改造 Dashboard联动成本略高对比下来Nacos 推模式解决了“规则谁能看到、谁能改、什么时候生效”的完整问题这也是我后来坚持改掉默认链路的原因。2. 方案架构与核心设计2.1 一条完整规则链路拆解改造后的链路可以分成三层来看先是 Sentinel Dashboard再是 Nacos最后是接入方应用。Dashboard 上的所有操作包括新增、修改、删除不再直接打到客户端内存接口而是先从 Nacos 读规则列表合并计算结果后把整份规则发布到指定的 dataId。Nacos 收到新配置后向所有监听该 dataId 的客户端推送变更事件。客户端的NacosDataSource监听到变化把 JSON 内容解析成DegradeRule列表再交给DegradeRuleManager去更新。这里有个关键点推送的对象是整份规则列表不是单条规则。所以每次修改都要基于 Nacos 里的最新内容做合并不能拿一个只包含新规则的小数组直接覆盖否则会把旧规则全清掉。这个思路和把整个配置文件提交到配置中心是一样的单条更新只是业务层面的表达存储层始终是一整份 JSON。2.2 只改客户端的方案为什么不够有人可能会问客户端接入 Nacos 数据源不是就能持久化了吗确实接入数据源之后应用能直接从 Nacos 加载规则启动不再丢。但如果 Dashboard 不改造你在界面上新增一条降级规则时它还是走默认内存接口推给客户端规则不会主动出现在 Nacos 里。结果是应用重启后只有 Nacos 里原本存在的那批规则还在Dashboard 后来新增的全部丢失。所以要想真正实现“Dashboard 推送降级规则到 Nacos”必须同时解决两个方向的问题。一个是“读”Dashboard 展示规则时要能从 Nacos 读回来而不是读内存另一个是“写”Dashboard 保存规则时要写到 Nacos而不是推给客户端内存。只做客户端数据源只解决了“读”的一半“写”的入口反而是断的。2.3 dataId、groupId 与命名空间怎么划分整理这条链路时最容易糊弄的就是 dataId 到底怎么命名。我建议以应用名作为主键采用${spring.application.name}-degrade-rules作为 dataId。group 可以统一用一个独立分组比如SENTINEL_GROUP避免和业务配置文件混在一起后续查找也方便。如果公司内部按环境拆了 namespace那么同一个应用、同一个 dataId 在测试环境和生产环境会分别有自己的内容。Dashboard 在连接 Nacos 时要注意和客户端使用同一个 namespace否则可能遇到“界面里能看到规则客户端也能接到新配置但两边内容对不上”的情况。3. 从零到一具体实现步骤3.1 被保护应用先接上 Nacos 数据源先说客户端侧也就是需要熔断降级的应用。如果项目用 Spring Cloud Alibaba通常已经有spring-cloud-starter-alibaba-sentinel但为了把规则接到 Nacos还需要显式引入sentinel-datasource-nacos。dependency groupIdcom.alibaba.csp/groupId artifactIdsentinel-datasource-nacos/artifactId version1.8.6/version /dependency配置方面关键是在spring.cloud.sentinel.datasource下面声明一个数据源并指定rule-type。我习惯把数据源叫degrade配置如下spring: application: name: order-service cloud: sentinel: transport: dashboard: localhost:8858 port: 8719 datasource: degrade: nacos: server-addr: 127.0.0.1:8848 namespace: dev group-id: SENTINEL_GROUP >NacosDataSourceListDegradeRule dataSource new NacosDataSource( 127.0.0.1:8848, SENTINEL_GROUP, order-service-degrade-rules, source - JSON.parseArray(source, DegradeRule.class) ); DegradeRuleManager.register2Property(dataSource.getProperty()); dataSource.init();原生方式需要自己定义 JSON 和DegradeRule的转换出错时也要自己排查。Spring Cloud Alibaba 配置方式会直接使用内部转换器大多数场景已经够用。3.2 降级规则 JSON 的内容格式降级规则在 Nacos 里存的是 JSON 数组每条规则对应一个资源。一个可供直接参考的格式如下[ { resource: GET:/order/{id}, grade: 1, count: 0.5, timeWindow: 10, minRequestAmount: 5, statIntervalMs: 1000 } ]这里几个字段值得展开说。resource是规则对应的资源名在代码里用SphU.entry(资源名)保护的地方资源名必须和这里的值完全一致。grade是降级策略0 表示按响应时间降级count是阈值毫秒数1 表示按异常比例降级count取 0 到 1 之间的小数2 表示按异常数降级count是一个统计周期内的异常总数。timeWindow是熔断后多长时间进入半开恢复单位秒。minRequestAmount是触发熔断前所需的最小请求数避免请求量太少时误判。不同版本字段含义基本稳定但如果在旧版本上做过二次开发加载前最好打印一次规则对象确认字段有没有被反序列化丢掉。3.3 Dashboard 改造让读写都走 Nacos这是工程量最大、也最值得复刻的一部分。这里以社区里比较通用的改造思路为例。Dashboard 的规则管理最终会落到两个角色DynamicRuleProvider负责读DynamicRulePublisher负责写。不同版本的类名可能有差异比如降级规则在部分 1.8.x 版本里是DegradeRuleStore接口但改造思路完全一样就是把它替换成基于 Nacos 的实现。先写一个 Nacos 规则读取器负责从 Nacos 拉取指定应用的最新规则列表Component(degradeRuleNacosProvider) public class DegradeRuleNacosProvider implements DynamicRuleProviderListDegradeRuleEntity { private final ConfigService configService; public DegradeRuleNacosProvider(ConfigService configService) { this.configService configService; } Override public ListDegradeRuleEntity getRules(String appName) throws Exception { String dataId appName -degrade-rules; String content configService.getConfig(dataId, SENTINEL_GROUP, 3000); if (content null || content.trim().isEmpty()) { return new ArrayList(); } return JSON.parseArray(content, DegradeRuleEntity.class); } }再写一个 Nacos 规则发布器Component(degradeRuleNacosPublisher) public class DegradeRuleNacosPublisher implements DynamicRulePublisherListDegradeRuleEntity { private final ConfigService configService; public DegradeRuleNacosPublisher(ConfigService configService) { this.configService configService; } Override public void publish(String appName, ListDegradeRuleEntity rules) throws Exception { String dataId appName -degrade-rules; configService.publishConfig(dataId, SENTINEL_GROUP, JSON.toJSONString(rules), json); } }接着在 Dashboard 的配置类里把默认创建的 Provider / Publisher 替换成上面这两个 Bean。这样新增规则时Controller 调用publisher.publish(app, rules)把完整列表写到了 Nacos展示规则时Provider 又从 Nacos 把列表读出来。Controller 里的具体方法改动并不复杂核心逻辑有三段。新增操作先getRules(appName)把新规则追加到列表尾部再publish修改操作先找到对应 id 的规则替换字段再publish删除操作先过滤掉对应 id 再publish。这三段操作的关键是每次都必须先读最新列表否则两个 Dashboard 实例同时操作时后写入的一方会把另一方的修改覆盖掉。3.4 Nacos 里预先建好空规则配置改造后有一个细节容易漏如果某个 dataId 在 Nacos 里根本不存在应用的NacosDataSource启动时拉不到配置会处于一个没有规则的状态。Dashboard 第一次读取时也会拿到 null如果代码里没有判空页面可能直接报错。建议在改造完成后先在 Nacos 控制台里给每个应用建一个初始配置。内容就是一个 JSON 数组哪怕没有规则也要写成[]。这一条既能让应用启动时干干净净也能让 Dashboard 的规则页面正常打开后续修改只是基于这个空数组不断更新。3.5 启动后的验证路径配置完成后不要急着把全套默认接口关掉先验证链路再收尾。我建议按这个顺序验证启动 Nacos、Dashboard 和业务应用确认业务日志没有数据源加载异常。在 Nacos 控制台把 dataId 配置成[{resource:GET:/order/{id},grade:0,count:300,timeWindow:10}]发布后隔几秒然后在业务应用里触发相应接口看到规则能生效。回到 Dashboard确认规则列表中能显示上面这条规则。如果显示不出来优先检查 Provider 是否真的被注入进去了可以在启动日志里 grep Provider 的类名。在 Dashboard 页面新增一条降级规则立刻去 Nacos 控制台查看对应 dataId 的配置内容看规则内容是否新增。若新增成功说明写链路通了。业务应用重启一次等应用起来后请求接口确认规则依然存在说明读链路也没问题。4. 常见问题与排查记录4.1 页面新增规则后 Nacos 里没有变化这个问题的原因大多是 Bean 没有替换成功。Dashboard 源码里如果默认 Provider 是通过条件装配加载的新建的 Nacos Provider 没有生效Controller 调用的还是内存版实现。排查时可以在 Provider 和 Publisher 的构造方法里加日志改完规则后看一眼输出。如果日志没有出现说明 Bean 优先级或者包扫描范围出了问题把自定义 Bean 移到能找到的位置再试。4.2 客户端能启动但规则始终不生效通常不是链路断了而是配置和实际请求的资源名对不上。我在实际排障中遇到最多的情况是规则里的resource写成了接口 URL但代码里用不同名称做的SphU.entry埋点资源名字面值不一致规则自然永远匹配不到。排查这类问题可以在客户端启动后请求 transport 接口curl http://127.0.0.1:8719/getRules?typedegrade确认规则有没有真正进入客户端。有规则但是不生效下一步就看日志里对应的资源名或者在本地把DegradeRuleManager.getRules()打出来对比。只要资源名对齐大多数情况下规则会立刻生效。4.3 多个实例同时修改导致规则互相覆盖Nacos 的publishConfig是整体覆盖没有按单条规则做增量合并。两个运维人员同时在 Dashboard 上操作后提交的人如果基于旧列表发布会把前一个人的新增覆盖掉。解决思路有两个一是在改造后的 Controller 层加乐观锁基于读取时拿到的版本号或更新时间判断二是开发流程上收紧权限只允许一个人维护一套规则。如果暂时不想加锁也可以把规则全部交给配置中心人工维护Dashboard 只做展示写操作关闭但这个目标和“从 Dashboard 推送”有些冲突。4.4 依赖版本冲突导致数据源不自动加载sentinel-datasource-nacos的版本最好和sentinel-core对齐。如果项目里 Nacos client 版本被更高版本覆盖而数据源里的监听 API 在旧版本里行为不一致常见表现是配置发布后客户端不感知或者应用启动时连接失败。这类问题需要先统一 BOM 管理或者显式把 Nacos client 的版本排除再看看日志中异常堆栈一般能定位到是版本不兼容还是连接配置错误。4.5 鉴权开启后 Dashboard 连不上 Nacos如果 Nacos 服务端开启了鉴权Dashboard 创建ConfigService时需要传入用户名和密码。很多改造例子都没提到这一点我第一次接入时就卡在启动阶段代码一直报 403。解决办法是把 Nacos 账号密码作为 properties 传入配置服务客户端的 datasource 配置里同样要加上对应的username和password字段保证两端使用同一个身份访问。5. 落地后的几点实操心得这条链路稳定跑了一段时间后我最大的感受是把降级规则推到 Nacos收益不只是“重启不丢”这一个点而是规则开始具备配置管理的属性。所有规则变更都在 Nacos 里留下历史版本哪次改坏了可以一键回滚这在默认内存模式下根本做不到。每次上线前我都能直接看 Nacos 里的规则内容和业务版本对应关系比登录 Dashboard 一台台检查可靠多了。有一个细节是改造时不要把所有逻辑都收进一个模块里。下一步如果要做规则灰度可以把 Nacos 里的规则按分组继续拆比如先给边缘实例发布新规则观察一批流量再放开全部。监听方和应用负责人之间最好也约定好dataId 一旦确定就不要随意改动否则服务器端存了一堆没人用的规则时间长了根本分不清哪些数据是有效的。最后再分享一个排查技巧在 Nacos 控制台发布规则时先把内容格式化成一个合法 JSON 数组再发布。曾经遇到过有人在 Nacos 页面上手填配置末尾多了一个逗号导致客户端反序列化直接失败Dashboard 列表也读不出来。从那以后我所有规则变更都固定用 JSON 数组的结构去发布宁可内容简单一点也不要留歧义。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑