Sentinel 规则持久化到 Nacos 实践(网关篇 + 普通应用篇)
本文基于 Sentinel 1.8.9 社区版 dashboard 源码改造,实现网关流控规则与 API 分组规则的 Nacos 持久化,并给出普通 Spring MVC 应用的接入方案。文中涉及的所有 IP、账号、密码均已脱敏,请替换为你自己的环境信息。
1. 背景
社区版 Sentinel dashboard 的规则存储是纯内存:
dashboard UI 改规则 ──HTTP(command API)──→ 客户端 JVM 内存
存在两个丢失点:
- 客户端重启 → 内存规则丢失;
- dashboard UI 改的规则不落 Nacos → 即使客户端配了 datasource-nacos,UI 改的那份也只在内存,重启后回退到 Nacos 旧规则。
本次改造实现 push 模式 :dashboard UI 修改规则后全量持久化到 Nacos 配置中心 ,客户端通过 sentinel-datasource-nacos 监听变更实时生效,任意一方重启规则均不丢。
dashboard UI 改规则 ──publishConfig──→ Nacos 配置中心 ──gRPC 监听──→ 客户端(sentinel-datasource-nacos)
(持久化,重启不丢) (变更秒级生效 / 重启拉全量)
改造范围 :先实现网关类规则(网关流控规则 gw-flow + API 分组定义 gw-api),再给出普通应用的流控/熔断规则扩展方案。
2. 版本与环境事实
| 项 | 值 |
|---|---|
| Nacos Server | 3.0.3 |
| dashboard | Sentinel 1.8.9 源码本地编译,端口 8083 |
| 网关应用 | Spring Boot 3.5.0 / Spring Cloud 2025.0.0 / spring-cloud-alibaba 2025.0.0.0 ,传递 nacos-client 3.0.3 |
| dashboard nacos-client | 本次新增 3.0.3(与 Server/客户端三方对齐) |
| group 选型 | DEFAULT_GROUP |
| namespace(test) | <NAMESPACE_ID>(注意:客户端连的是命名空间 ID,不是名称) |
| Nacos 地址(test) | <NACOS_SERVER_ADDR>(gRPC 端口自动 +1000,需放通) |
请将
<NACOS_SERVER_ADDR>替换为你的 Nacos 地址,格式如10.0.0.1:8848;<NAMESPACE_ID>替换为实际命名空间 ID。
3. Nacos 侧配置
在目标 namespace 下预创建 2 个配置(不预创建时客户端首次启动拉取会报 config not exist):
| dataId | group | 初始内容 |
|---|---|---|
yc-gateway-gw-flow-rules |
DEFAULT_GROUP | [] |
yc-gateway-gw-api |
DEFAULT_GROUP | [] |
dataId 命名约定:{app}-gw-flow-rules / {app}-gw-api,app 即客户端应用名(如 yc-gateway)。
gw-flow 规则 JSON 示例 (数组,字段与 core GatewayFlowRule 一致):
json
[
{
"resource": "yc-business-route",
"resourceMode": 0,
"grade": 1,
"count": 100,
"intervalSec": 1,
"controlBehavior": 0,
"burst": 0
}
]
gw-api JSON 示例 (字段与 core ApiDefinition 一致):
json
[
{
"apiName": "message-remind-api",
"predicateItems": [
{ "pattern": "/api/messageRemind/", "matchStrategy": 1 }
]
}
]
matchStrategy:0=精确 1=前缀 2=正则。dashboard 页面上配好规则后内容会自动覆盖。
4. dashboard 改造(sentinel 源码)
4.1 改动文件清单
| 文件 | 类型 | 说明 |
|---|---|---|
sentinel-dashboard/pom.xml |
修改 | 新增 com.alibaba.nacos:nacos-client:3.0.3 |
sentinel-dashboard/src/main/resources/application.properties |
修改 | 新增 nacos.* 连接配置(支持环境变量覆盖) |
dashboard/rule/nacos/NacosConfigSupport.java |
新增 | ConfigService 懒加载单例(gRPC 长连接);dataId 拼接约定 |
dashboard/rule/nacos/GatewayFlowRuleNacosProvider.java |
新增 | 读 Nacos:核心规则 JSON → dashboard 实体 |
dashboard/rule/nacos/GatewayFlowRuleNacosPublisher.java |
新增 | 写 Nacos:dashboard 实体 → 核心规则 JSON 全量覆盖 |
dashboard/rule/nacos/GatewayApiNacosProvider.java |
新增 | 读 Nacos(API 分组,含接口类型手工解析) |
dashboard/rule/nacos/GatewayApiNacosPublisher.java |
新增 | 写 Nacos(API 分组) |
dashboard/controller/gateway/GatewayFlowRuleController.java |
修改 | 读写从"直调客户端"改为 Nacos |
dashboard/controller/gateway/GatewayApiController.java |
修改 | 同上 |
4.2 关键设计点
-
存储格式 = 客户端反序列化格式
Nacos 中存核心规则对象 (
GatewayFlowRule/ApiDefinition)的 JSON 数组;dashboard 读写均经entity.toGatewayFlowRule()/fromGatewayFlowRule(...)转换,剔除id/app/ip/port/gmtCreate/gmtModified等 dashboard 专属字段。严禁直接序列化 dashboard 实体(嵌套 rule 结构会导致客户端解析失败)。 -
gw-api 接口类型反序列化坑
ApiDefinition.predicateItems是接口类型Set<ApiPredicateItem>,fastjson(1.2.83_noneautotype,autoType 已禁用)无法直接反序列化------dashboard 侧GatewayApiNacosProvider手工解析为ApiPathPredicateItem(与客户端UpdateGatewayApiDefinitionGroupCommandHandler#parseJson同款);客户端 Nacos 数据源侧由 spring-cloud-alibaba-sentinel-gateway 自带的ApiPredicateItemDeserializer(Jackson 自定义反序列器,2025.0.0.0 已验证存在)处理,因此客户端正常。 -
publish 失败显式报错
Nacos 成为规则真源后,写 Nacos 失败若仍返回成功,规则会在下次刷新时"消失"(UI 与 Nacos 不一致);因此增/删/改后 publish 失败直接向前端返回
publish rules to nacos fail。 -
内存仓库仍保留
InMemGatewayFlowRuleStore仅用于:list.json 刷新时给规则分配展示 id、apiName 重复校验、增删改后的全量规则收集。id 仅 dashboard 会话内有效,每次进入页面重新分配。 -
全量覆盖语义
每次增/删/改把该应用全部规则写回 dataId(官方 push 模式标准做法);多人同时改规则存在互相覆盖风险(规则量小可接受,见 §8)。
-
前端零改动
Controller 接口签名不变(
/gateway/flow/*.json、/gateway/api/*.json)。 -
ConfigService 线程安全
NacosConfigSupport.getConfigService()采用 DCL 双重检查锁(volatile+synchronized)保证单例,gRPC 长连接创建成本高,全局复用一个实例;group不进 Properties(Nacos group 是 API 调用时传参,不在连接级别预设)。
4.3 application.properties 新增配置
properties
# ========== Nacos 规则持久化(push 模式改造) ==========
nacos.server-addr=${NACOS_SERVER_ADDR:<NACOS_SERVER_ADDR>}
# namespace 必须传命名空间 ID(不是名称),当前为 test 环境,生产通过 NACOS_NAMESPACE 注入
nacos.namespace=${NACOS_NAMESPACE:<NAMESPACE_ID>}
nacos.group=${NACOS_GROUP:DEFAULT_GROUP}
nacos.username=${NACOS_USERNAME:nacos}
nacos.password=${NACOS_PASSWORD:<NACOS_PASSWORD>}
生产部署时通过环境变量覆盖:
| 环境变量 | 默认值 | 说明 |
|---|---|---|
NACOS_SERVER_ADDR |
<NACOS_SERVER_ADDR> |
Nacos 地址 |
NACOS_NAMESPACE |
test 环境命名空间 ID | 必须传 ID |
NACOS_GROUP |
DEFAULT_GROUP | 一般不动 |
NACOS_USERNAME |
nacos | Nacos 鉴权用户 |
NACOS_PASSWORD |
<NACOS_PASSWORD> |
生产必填,否则 publish 鉴权失败 |
请替换
<NACOS_SERVER_ADDR>、<NAMESPACE_ID>、<NACOS_PASSWORD>为你自己的值。
4.4 打包与部署
bash
# 必须用 JDK 17 构建(maven-compiler-plugin 3.12 的 --release 参数与 JDK 8 不兼容)
export JAVA_HOME=/path/to/jdk-17
mvn -pl sentinel-dashboard -am clean package -DskipTests
# 产物:sentinel-dashboard/target/sentinel-dashboard.jar(fat jar)
部署方式不变(java -jar 或 Dockerfile),需补充:
- 注入
NACOS_PASSWORD等环境变量; - dashboard 所在 Pod → Nacos gRPC 端口(主端口+1000) 网络放通;
- 重启 dashboard 后规则自动从 Nacos 恢复。
5. 网关侧配置
依赖已就位(sentinel-datasource-nacos),无需改代码,仅修改 application-nacos-test.yml 的 spring.cloud.sentinel:
yaml
spring:
cloud:
sentinel:
eager: true
transport:
dashboard: <SENTINEL_DASHBOARD_ADDR> # 替换为 dashboard 地址
datasource:
gw-flow:
nacos:
server-addr: <NACOS_SERVER_ADDR>
namespace: <NAMESPACE_ID>
group-id: DEFAULT_GROUP
data-id: yc-gateway-gw-flow-rules
username: nacos
password: ${NACOS_PASSWORD}
rule-type: gw-flow # 注意:在 nacos 内部
gw-api:
nacos:
server-addr: <NACOS_SERVER_ADDR>
namespace: <NAMESPACE_ID>
group-id: DEFAULT_GROUP
data-id: yc-gateway-gw-api
username: nacos
password: ${NACOS_PASSWORD}
rule-type: gw-api-group # 注意:枚举值为 gw-api-group
scg:
enabled: true
webflux:
enabled: false # 必须关闭,避免重复埋点
要点:
rule-type必须写在nacos块内部 ,且取值为 SCARuleType固定枚举:网关流控gw-flow、API 分组gw-api-group(不是gw-api)。- 属性名是
group-id、data-id(非group、dataId)。 NACOS_PASSWORD建议通过环境变量注入。- 客户端懒加载/监听由 spring-cloud-alibaba 自动完成。
6. 验证步骤
- dashboard 启动日志确认:
Nacos config service initialized, serverAddr=..., namespace=..., group=DEFAULT_GROUP - 网关启动日志确认 Nacos 数据源加载(
[SentinelDataSourceHandler]/ NacosDataSource 注册),无 config-not-exist 报错; - dashboard UI(网关流控规则页)新增一条规则 → Nacos 控制台查看
yc-gateway-gw-flow-rules配置内容已更新; - 网关日志出现规则更新(
[GatewayRuleManager]/[GatewayApiDefinitionManager] ... updated); - 重启 dashboard → UI 上规则仍在;重启网关 → 规则仍在;
- 压测验证限流阈值生效(可选)。
7. 普通应用(非网关)接入 Sentinel 改造步骤
7.1 客户端接入(每个应用)
① 引依赖 (版本由父 pom 的 spring-cloud-alibaba-dependencies 统一管理):
xml
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
② 配置数据源 (application-nacos-test.yml):
yaml
spring:
cloud:
sentinel:
eager: true
transport:
dashboard: <SENTINEL_DASHBOARD_ADDR>
datasource:
flow:
nacos:
server-addr: <NACOS_SERVER_ADDR>
namespace: ${NACOS_NAMESPACE:<NAMESPACE_ID>}
group-id: DEFAULT_GROUP
data-id: yc-business-flow-rules
username: nacos
password: ${NACOS_PASSWORD}
rule-type: flow
degrade:
nacos:
server-addr: <NACOS_SERVER_ADDR>
namespace: ${NACOS_NAMESPACE:<NAMESPACE_ID>}
group-id: DEFAULT_GROUP
data-id: yc-business-degrade-rules
username: nacos
password: ${NACOS_PASSWORD}
rule-type: degrade
③ Nacos 预创建配置 :在对应 namespace 下创建 yc-business-flow-rules、yc-business-degrade-rules(group=DEFAULT_GROUP,初始内容 [])。
④(可选)代码级资源定义 :Web 接口自动埋点(资源名=URL),无需代码;非 HTTP 入口可使用 @SentinelResource 注解。
7.2 dashboard 持久化改造(一次性)
前提:已完成 §4 的网关改造(NacosConfigSupport 及公共设施已就位)。
① NacosConfigSupport 增加普通规则 dataId 约定:
java
public static final String FLOW_DATA_ID_SUFFIX = "flow-rules";
public static final String DEGRADE_DATA_ID_SUFFIX = "degrade-rules";
public static String flowDataId(String app) {
return app + "-" + FLOW_DATA_ID_SUFFIX;
}
public static String degradeDataId(String app) {
return app + "-" + DEGRADE_DATA_ID_SUFFIX;
}
② 新增 Provider/Publisher:复制网关版改 3 处(核心规则类型、dataId 方法、实体转换)。
③ 改 Controller :将 FlowControllerV1、DegradeController 中的 sentinelApiClient 调用替换为 Nacos Provider/Publisher,并同步返回 publish 结果。
④ 重新打包部署 dashboard,前端零改动。
7.3 规则类型总表
| 规则类型 | rule-type(客户端) | dashboard Controller | dataId 约定 | 实体转换 |
|---|---|---|---|---|
| 流控 | flow |
FlowControllerV1 |
{app}-flow-rules |
FlowRuleEntity.fromFlowRule / toRule |
| 熔断降级 | degrade |
DegradeController |
{app}-degrade-rules |
DegradeRuleEntity.fromDegradeRule / toRule |
| 授权 | authority |
AuthorityRuleController |
{app}-authority-rules |
同款转换 |
| 系统保护 | system |
SystemController |
{app}-system-rules |
同款转换 |
| 热点参数 | param-flow |
ParamFlowRuleController |
{app}-param-flow-rules |
同款转换(注意 7.4) |
建议只做 flow + degrade 两类(覆盖 80%+ 场景)。
7.4 param-flow 的接口反序列化坑
ParamFlowRule.paramItemList 是接口类型 List<ParamFlowItem>,dashboard 侧需手工解析;客户端侧 spring-cloud-alibaba 没有现成的反序列化器,需自定义 Converter 或改用其他方案。
7.5 存量规则迁移与上线顺序
- 改造上线前,从旧 dashboard UI 导出现网存量规则 → 整理成对应 dataId 的核心规则 JSON 数组 → 预写入 Nacos;
- 上线顺序:先发改造后的 dashboard,再改各应用 yml 并重启;
- 每接入一个应用,按 §6 验证清单全链路走一遍。
8. 注意事项与已知限制
- 三元组必须一致:dataId / group / namespace 在 dashboard 与客户端两端完全一致。
- namespace 传 ID 不传名称。
- 全量覆盖并发风险:push 模式下"最后写入者胜",规则变更建议固定负责人操作。
- dashboard 多实例:建议单实例部署,多实例写同一 dataId 无一致性问题,但 UI 规则 id 各自分配。
- 鉴权 :dashboard
nacos.password生产必须注入,否则 publish 时 Nacos 401。 - 网络:dashboard → Nacos 主端口 + gRPC 端口;客户端 → Nacos 同样端口。
- dashboard 重启后的规则 id 变化:id 仅用于前端定位,重启后重新编号,不影响功能。
- 规则内容校验:Nacos 上手工改 JSON 时若字段非法,客户端解析后由 core 校验拒绝加载,建议优先在 UI 上改。
- 规则同步行为:Nacos 是唯一真源,UI 改规则或 Nacos 控制台改规则都会同步到客户端;dashboard 页面停留期间若 Nacos 被手工修改,后续编辑可能覆盖手工修改(last-writer-wins)。
- ConfigService 创建失败无降级:Nacos 不可达时 dashboard 页面报错,不会回退到旧的内存模式。
- group 属性不进 Properties:group 通过 API 调用时传递。
- Nacos 配置预创建 :建议预创建空数组
[],避免首次启动 warn 日志。
9. 总结
通过将 Sentinel Dashboard 的规则存储从内存迁移到 Nacos,实现了规则持久化和动态同步,解决了客户端和 Dashboard 重启后规则丢失的问题。本文给出了网关规则的完整改造方案,并提供了普通应用接入的扩展指南。改造后,规则管理更加可靠,适合生产环境使用。
注意:文中所有 IP、账号、密码均为示例占位,实际部署时请替换为你的环境信息。