简介:面向微服务架构开发与运维人员,这份资源是 Sentinel Dashboard 1.8.6 与 Nacos 集成、对接 Gateway 限流的参考实现。核心思路是在 resources 下的 application.properties 中修改 Nacos 连接参数,使流控规则能动态同步到 Nacos,从而通过控制台统一管理下游网关的限流策略,适合需要落地服务治理方案的中高级工程师。压缩包共 568 个文件、约 26.25MB,包括 151 个 Java 源文件、170 个 class 编译类,覆盖 GatewayFlowRuleController、ParamFlowRuleController、ClusterConfigService 等关键控制器与集群配置服务,同时含有前端控制台所需的 js、html、css 以及 xml、properties 等配置,便于结合源码理解规则下发、参数流控和集群管控的实现细节。已有 194 人学习,目录结构相对完整,既可直接部署体验,也可作为二次开发或异常排查的参考。 说起 Sentinl Dashboard 和 Nacos 的集成,做微服务的同学应该都不陌生,但落在 1.8.6 这个版本上,想把 Dashboard 当规则管理后台、还要对接 Gateway 的限流规则,默认是做不到的。默认情况下规则全存在本地内存里,Dashboard 一重启规则就没了,网关服务的限流规则也跟 Dashboard 完全脱节。这篇文章我会把整套改造过程完整捋一遍,从为什么需要做持久化,到源码级改造 Dashboard,再到 Gateway 接入 Nacos 数据源和限流规则下发,适合正在用 Spring Cloud Alibaba + Gateway + Nacos 做微服务网关限流、又不想手动维护一堆规则文件的同学参考。
1. 为什么把 Sentinel Dashboard 1.8.6 绑到 Nacos 上
1.1 原生 Dashboard 的规则存储局限
默认情况下,Sentinel Dashboard 的所有规则都存在 JVM 内存里。你在页面上加了一条流控规则,数据落在服务端的内存 Map 中;客户端上报心跳后,Dashboard 再从内存里把规则列表拼出来渲染到前端。这套机制在单机演示时没问题,可到了生产环境就是大麻烦:Dashboard 一重启,规则全没。更难受的是,如果团队里同时有多个环境、多套网关,规则只能一台台手工配置,根本没有统一的版本概念。我最早在测试环境用原生 Dashboard 配了十几条 Gateway 限流规则,一次服务器迁移全丢了,从那以后我就下定决心把规则存储从内存换到 Nacos。
1.2 Nacos 作为规则存储能带来什么
规则持久化到 Nacos 之后,第一个收益就是规则不再跟着 Dashboard 的进程走。Nacos 本身就是配置中心,天然提供配置版本管理、历史变更记录、灰度发布这些能力;线上如果发现某条限流规则太激进,直接把 Nacos 里的配置回滚到上一版就行,不需要重新在页面上一遍遍点。第二个收益是客户端可以主动订阅数据源。Gateway 服务启动时会从 Nacos 拉规则,后续 Nacos 配置变更也会实时推给网关,限流阈值调整从分钟级变成秒级生效。第三个收益是 Dashboard 可以随时重启,甚至替换成新版本,只要 Nacos 里的规则还在,启动完成后自动把规则拉回来,业务无感知。
1.3 为什么特别强调对接 Gateway 限流
把限流放在网关层,主要是为了在流量进入后端服务之前就把它拦住。假如只在某个订单服务上配限流,请求已经穿过了网关、路由到了业务服务,这时候再拒绝流量,服务的线程池和数据库连接已经被占用了。网关限流是在入口做拦截,一个路由规则能保护后面所有下游服务。但 Gateway 的规则在 Sentinel Dashboard 里是单独管理的,普通流控规则对应的FlowRule和网关流控规则GatewayFlowRule走的是两套 Controller,持久化时也要分别实现 provider 和 publisher。如果只改了普通流控规则的持久化,Gateway 规则那块依然回退到内存存储,正好是很多教程里没讲透的地方。
2. 版本选型与基础环境准备
2.1 依赖版本组合参考
我在改造时采用的版本组合如下,实测能稳定跑通:
| 组件 | 版本 | 说明 |
|---|---|---|
| Spring Boot | 2.5.12 | 不要用 3.x,Sentinel 1.8.6 对 Spring Boot 3 支持不完整 |
| Spring Cloud Alibaba | 2021.0.1.0 | 包含 Sentinel 相关 starter |
| Nacos Server | 2.2.1 | 用 2.x,1.x 的鉴权和配置推送体验差很多 |
| sentinel-dashboard | 1.8.6 | 需要自己源码改造 |
| sentinel-datasource-nacos | 1.8.6 | Gateway 客户端侧接入 |
这个组合里最需要守住的一点是 Spring Cloud Alibaba 版本别追新。有些同学一看有 2023 版本就直接用,结果 Gateway 集成 Sentinel 的那套 starter 包路径都变了,后续排查成本很高。
2.2 Nacos 服务启动与核对
先从 Nacos 官网下载对应版本,解压后直接执行启动脚本。Linux / macOS 下是startup.sh -m standalone,Windows 下是startup.cmd -m standalone。启动完成后访问http://localhost:8848/nacos,默认账号密码是nacos/nacos,能正常登录说明服务没问题。生产环境建议把 Nacos 的配置存到 MySQL,不然 Nacos 自身一重启,你辛辛苦苦攒的命名空间、配置、用户数据一样会丢,这点很多人容易忽略。
2.3 改造前先捋清楚规则数据流
在动手改代码之前,建议先把规则流转方向在脑子里过一遍。Dashboard 页面点保存,Controller 拿到请求后调用的是 publisher,publisher 把规则序列化成 JSON 写到 Nacos 的指定 dataId;Gateway 客户端通过sentinel-datasource-nacos订阅同一个 dataId,Nacos 配置一变,客户端本地规则自动刷新,后面的请求就按新规则执行。整个过程里 Dashboard 只是编辑器和写入器,它不再是规则的唯一持有者。理解了这个数据流,后面看代码时就不会被一堆 Controller 绕晕。
3. Dashboard 集成 Nacos 的源码改造实录
3.1 源码准备与改造目标
先把仓库拉下来,切到1.8.6的 tag,改造只涉及sentinel-dashboard这一个模块。在 1.8.6 版本里,规则存储已经抽象出了DynamicRuleProvider<T>和DynamicRulePublisher<T>两个接口,默认是内存实现。我们新增一套 Nacos 实现,再把 Controller 里注入的内存实现替换成 Nacos 实现,改动范围很集中。
注意:1.8.6 和旧版本不同,FlowControllerV1仍然直接操作内存,FlowControllerV2才走 provider / publisher 扩展点。所以改造时要确认前端页面调用的是哪个 Controller,如果 V1 不走扩展点,后面规则写了也没效果。
3.2 引入 Nacos 客户端依赖
在sentinel-dashboard/pom.xml中增加:
<dependency> <groupId>com.alibaba.nacos</groupId> <artifactId>nacos-client</artifactId> <version>2.2.1</version> </dependency>如果 Nacos 服务端用了更老的版本,2.x 客户端大多数情况下也能兼容,但建议尽量对齐版本。
3.3 规则 dataId 与分组规划
统一规则 dataId 和 groupId 非常重要。我一般按应用名-规则类型命名 dataId,group 统一叫SENTINEL_GROUP。放在常量类里,Dashboard 和 Gateway 两端用同一套约定,避免对不上。比如应用名gateway-service,那普通流控规则就是gateway-service-flow-rules,网关流控规则是gateway-service-gateway-flow-rules,API 分组是gateway-service-gateway-api-rules。
3.4 Provider 与 Publisher 实现
以普通流控规则为例,读取规则的 Provider 长这样:
@Component("flowRuleNacosProvider") public class FlowRuleNacosProvider implements DynamicRuleProvider<List<FlowRuleEntity>> { @Autowired private ConfigService configService; @Override public List<FlowRuleEntity> getRules(String appName) throws Exception { String dataId = appName + NacosConfigUtil.FLOW_DATA_ID_POSTFIX; String rules = configService.getConfig(dataId, NacosConfigUtil.GROUP_ID, 3000); if (StringUtil.isEmpty(rules)) { return new ArrayList<>(); } return JSON.parseArray(rules, FlowRuleEntity.class); } }写入规则的 Publisher 长这样:
@Component("flowRuleNacosPublisher") public class FlowRuleNacosPublisher implements DynamicRulePublisher<List<FlowRuleEntity>> { @Autowired private ConfigService configService; @Override public 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, JSON.toJSONString(rules) ); } }网关流控规则和 API 分组规则也是同样的套路,只是实体类换成GatewayFlowRuleEntity和ApiDefinitionEntity,dataId 后缀换成对应的常量。建议直接照着FlowRule的实现复制出来改,不要试图省事搞一个通用类,后面调试时最简单的代码反而最不容易出错。
3.5 注入替换与编译打包
在FlowControllerV2里,默认注入的是flowRuleProvider和flowRulePublisher,要把注入点换成flowRuleNacosProvider和flowRuleNacosPublisher。GatewayFlowRuleController和GatewayApiController同理。改完后执行mvn clean package,生成新的 Dashboard Jar 包。启动时记得配置 Nacos 地址,我一般用环境变量SENTINEL_NACOS_ADDR,启动命令大致如下:
java -jar sentinel-dashboard.jar \ -Dserver.port=8080 \ -Dcsp.sentinel.dashboard.server=localhost:8080 \ -Dproject.name=sentinel-dashboard \ -Dsentinel.nacos.serverAddr=localhost:8848注意:ConfigService的初始化要在 Dashboard 启动时就完成,建议在NacosConfigUtil中提供初始化方法,用静态块或启动监听器创建,避免第一次访问页面时才创建导致规则读取失败。
4. Gateway 接入 Sentinel 并打通限流
4.1 网关工程依赖
在 Gateway 工程的 pom.xml 里加上:
<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId> </dependency> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-datasource-nacos</artifactId> <version>1.8.6</version> </dependency>第一个 starter 提供 Sentinel 基础能力,第二个负责把 Sentinel 的过滤器挂到 Spring Cloud Gateway 的过滤器链上,第三个是 Nacos 数据源。三个缺一个,限流链路都不完整。
4.2 配置数据源指向 Nacos
在application.yml中配置:
spring: application: name: gateway-service cloud: sentinel: transport: dashboard: localhost:8080 port: 8719 datasource: flow: nacos: server-addr: localhost:8848 dataId: gateway-service-flow-rules groupId: SENTINEL_GROUP >[ { "id": 1, "appName": "gateway-service", "apiName": "order_api", "predicateItems": [ { "pattern": "/order/**", "matchStrategy": 0 } ] } ]matchStrategy为 0 表示精确匹配前缀,1 表示正则匹配。之后在 Dashboard 的网关流控规则里,资源名直接填order_api,这样限流规则就跟具体的 Gateway 路由解耦了。路由后面怎么调整,只要 API 前缀不变,规则就不用动。
4.4 验证限流效果
全部配置好后,先用单机压测工具快速验证。我习惯先配一条 QPS 阈值为 10 的网关规则,再用ab或 Jmeter 发 100 个并发请求,观察返回结果。命中限流时,Sentinel 默认返回的是Blocked by Sentinel: FlowException,配合自定义的BlockRequestHandler可以改成立即返回 JSON 错误体。这里提醒一下:设置过低的阈值容易在压测时把自己手误限流出问题,测试阶段建议从 50 或 100 开始往下压,逐步观察曲线。
5. 实战中踩过的坑与排查手册
5.1 Dashboard 页面没有 Gateway 流控规则入口
我在第一次改造完成后,登录 Dashboard 发现只有“流控规则”“降级规则”这些菜单,完全没有“网关流控规则”。原因是 1.8.6 的 Dashboard 前端菜单根据网关模式字段来控制,而后端服务里 Gateway 相关 Controller 因为缺少依赖没有被加载。解决方案是前后端一起排查:后端先确认GatewayFlowRuleController是否被扫描到,前端确认导航菜单配置里网关相关的可见性条件是否满足。
5.2 Nacos 里配置存在但网关限流不生效
最典型的场景是 Dashboard 写的 dataId 是gateway-service-flow-rules,网关的spring.cloud.sentinel.datasource里写的却是gateway-flow-rules,两边对不上。另一个常见问题发生在 JSON 格式上,FlowRuleEntity序列化时如果带了id、gmtModified这些额外字段,客户端反序列化部分场景下会抛异常。我的做法是尽量用精简的 JSON,publisher 里只发布核心字段,或者在客户端用统一的 JSON 解析配置。
5.3 Dashboard 保存规则后 Nacos 里始终没有数据
这种问题基本都出在 publisher 没有生效。常见原因有三个:注入点还是默认的内存实现,ConfigService初始化失败但异常被吞了,以及publishConfig调用参数里 dataId 或 groupId 写了空值。排查时先看 Dashboard 启动日志有没有 Nacos 连接成功的记录,再确认 Controller 里注入的 Bean 是哪个实现。
5.4 Dashboard 重启后页面规则为空
先说结论:这是 provider 没生效的典型表现,页面读取的规则仍然来自内存,而不是 Nacos。检查点主要是FlowControllerV2的 provider 注入、前端请求路径是否真的打到了 V2。有些教程直接改 V1,改完页面确实能读规则,但 V1 底层没有走 provider,等于绕过了持久化,看着像成功,实则白改。
5.5 Dashboard 和 Gateway 都连上 Nacos 后出现规则覆盖
这个坑比较隐蔽。它更多是使用习惯问题:如果 Dashboard 和网关客户端各自从 Nacos 加载规则,数据源是同一个,理论上不会重复。但一旦有人在 Dashboard 页面保存规则,Dashboard 会通过 publisher 把内存里的全量规则写回 Nacos;如果内存规则和 Nacos 已有规则不一致,就可能出现覆盖。建议把 Dashboard 侧的 Nacos 写入作为唯一写入源,网关侧数据源只做订阅和动态刷新,不要两边都维护同一份规则。
6. 关于这套方案的一些个人心得
6.1 规则模型尽量精简
我在实际项目里有一个很深的体会:FlowRuleEntity这种在 Dashboard 页面展示用的实体类,直接序列化到 Nacos 虽然后端能解析,但它包含了很多 UI 相关字段,例如gmtModified、id、app等。网关客户端用sentinel-datasource-nacos反序列化时,如果 JSON 里有这些多余字段,在严格模式下会直接失败。建议在 publisher 里做一层转换,或者干脆定义精简的 DTO,只保留 Sentinel 规则真正需要的字段。
6.2 生产环境用命名空间隔离
如果公司有多个环境,建议在 Nacos 里按环境建命名空间,然后把SENTINEL_GROUP、规则 dataId 都放到对应命名空间下。这样测试环境的限流规则不会影响到生产环境,也不会因为误操作互相覆盖。Dashboard 启动和 Gateway 配置里都要指定同一个命名空间,踩过一次“测试环境规则覆盖生产”的坑后就再也回不去了。
6.3 监控与告警不能只看 Dashboard
Dashboard 只是规则管理台,不是实时监控的唯一来源。Sentinel 的监控数据默认是 10 秒聚合一次推到 Dashboard,如果 Dashboard 重启时间比较长,这部分监控数据就会丢。建议把监控指标同时接到 Prometheus / Grafana,限流规则持久化解决了规则丢失问题,但监控数据还得靠另外一套链路保底。
最后分享一个实在的建议:如果你所在的项目组要从 0 搭建网关限流,不要先把所有规则一次性配好,先挑一个非核心接口把整条链路跑通——Dashboard 写规则、Nacos 存储、Gateway 订阅、压测出效果,再慢慢丰富规则。这条路我走过一遍,最费时间的从来不是代码,而是版本兼容和数据链路理解。希望这篇实战记录能帮你少踩几个我踩过的坑。
本文还有配套的精品资源,点击获取