
1. 为什么要在网关层做限流降级微服务架构跑起来之后最先扛不住的往往不是业务代码而是入口流量。我经历过好几次大促前的压测后端服务明明单机 QPS 能跑到三千结果网关一放量整个链路像多米诺骨牌一样往下倒。后来复盘发现问题不在某个服务本身而在于流量没有在入口被管住——一个慢接口把线程池占满上游调用方超时重试重试流量又打进来雪崩就这么开始了。这个项目标题拆开看是四件事SpringBoot 做基础工程、Gateway 做统一入口、Nacos 做配置与注册中心、Sentinel 做限流降级。四者组合起来解决的核心问题是在微服务入口处建立一道可控的流量闸门让系统在高压下有序降级而不是整体崩溃。适合谁参考正在从单体往微服务迁移的团队、已经上了网关但限流规则还散落在各服务里的开发者、以及想搞清楚 Sentinel 在网关场景和普通 Web 场景差异的同学。我先把结论摆出来网关层限流和业务层限流不是二选一而是分层防御。网关层负责挡住不该进来的流量业务层负责保护关键资源的并发。这个项目重点落在网关层因为它是所有外部请求的必经之路改动成本最低、收益最直接。下面我按实际搭建顺序把选型逻辑、配置细节、踩坑记录完整拆一遍。2. 整体架构设计与技术选型考量2.1 四个组件各自扮演什么角色很多人一上来就堆组件结果出了问题不知道是哪一层的事。我习惯先把职责边界划清楚组件职责在本项目中的具体作用SpringBoot基础工程框架承载网关应用、提供自动装配与启动能力Gateway统一流量入口路由转发、过滤器链、限流规则的执行点Nacos注册中心 配置中心服务发现、限流规则持久化与动态推送Sentinel流量治理限流、熔断降级、热点参数控制的规则引擎这里有个容易混淆的点Sentinel 的规则默认存在内存里应用重启就没了。所以必须借助 Nacos 做规则持久化让规则在 Nacos 里配置、通过数据源推送到 Sentinel。这是整个方案能落地生产的关键不是可选项。2.2 为什么限流放在 Gateway 而不是各业务服务我试过两种方案。方案 A 是每个业务服务自己引 Sentinel 做限流方案 B 是统一在 Gateway 做。实测下来方案 A 的问题是规则分散、口径不一A 服务限 100、B 服务限 200但入口总流量没人管等于每个房间都装了门锁却没装大门。方案 B 的优势在于统一口径所有外部流量经过网关限流阈值按路由维度配置一目了然。保护后端网关挡住超额流量后后端服务收到的请求量是可预期的线程池不会被瞬间打爆。动态生效规则改在 Nacos网关实时感知不用重启任何服务。当然方案 B 也有局限——它管不了服务间的内部调用。所以我的实践是网关层做粗粒度限流按路由/API 分组业务层对核心接口做细粒度限流按热点参数两层配合。这个项目先把网关层这套跑通。2.3 版本搭配的坑别随便升SpringBoot、Gateway、Nacos、Sentinel 四者的版本兼容性是个隐形雷区。我踩过最典型的一次SpringBoot 升到较新版本后Gateway 的spring-cloud-starter-gateway和 Sentinel 的网关适配包出现类冲突启动直接报NoSuchMethodError。原因是 Sentinel 网关适配包依赖的 Spring WebFlux 版本和 Gateway 自带的不一致。我的建议是锁定一套经过验证的组合比如 SpringBoot 2.7.x 配 Spring Cloud 2021.x、Spring Cloud Alibaba 2021.xNacos 用 2.x 稳定版Sentinel 用 1.8.x。升级任何一个之前先查 Spring Cloud Alibaba 的版本说明表别凭感觉升。下面配置示例都基于这套组合你换版本时对照官方兼容矩阵调整即可。3. 核心配置与实操落地3.1 工程依赖与基础配置网关工程的核心依赖就几个但顺序和排除项有讲究。spring-cloud-starter-gateway自带 WebFlux千万不要再引spring-boot-starter-web两者冲突会导致网关启动失败。这是新手最高频的翻车点。dependencies dependency groupIdorg.springframework.cloud/groupId artifactIdspring-cloud-starter-gateway/artifactId /dependency dependency groupIdcom.alibaba.cloud/groupId artifactIdspring-cloud-starter-alibaba-nacos-discovery/artifactId /dependency dependency groupIdcom.alibaba.cloud/groupId artifactIdspring-cloud-starter-alibaba-nacos-config/artifactId /dependency dependency groupIdcom.alibaba.csp/groupId artifactIdsentinel-spring-cloud-gateway-adapter/artifactId /dependency dependency groupIdcom.alibaba.csp/groupId artifactIdsentinel-datasource-nacos/artifactId /dependency /dependencies注意最后两个sentinel-spring-cloud-gateway-adapter是网关场景专用适配包它和普通 Web 场景的sentinel-starter不是一回事sentinel-datasource-nacos负责从 Nacos 拉规则。少引任何一个要么限流不生效要么规则无法持久化。3.2 路由与 Nacos 注册配置路由配置我倾向放在配置文件里而不是硬编码方便不同环境切换。核心是uri用lb://服务名走负载均衡服务名必须和 Nacos 里注册的一致。spring: application: name: api-gateway cloud: nacos: discovery: server-addr: 127.0.0.1:8848 config: server-addr: 127.0.0.1:8848 file-extension: yaml gateway: discovery: locator: enabled: true routes: - id: order-service uri: lb://order-service predicates: - Path/order/** filters: - StripPrefix1StripPrefix1的作用是转发时去掉第一段路径比如/order/create转发到后端变成/create。这个参数配错会导致后端 404排查时先看这里。discovery.locator.enabled打开后可以按服务名自动路由但生产环境我建议关掉用显式路由更可控避免意外暴露内部服务。3.3 Sentinel 网关限流规则怎么配网关限流和普通接口限流的规则模型不同。网关场景下Sentinel 用GatewayFlowRule核心参数是resource对应路由 ID 或 API 分组名和countQPS 阈值。这里有个关键理解网关限流的资源名默认是路由 ID不是 URL 路径。很多人配了/order/**发现不生效就是因为资源名对不上。Configuration public class GatewayFlowConfig { PostConstruct public void initGatewayRules() { SetGatewayFlowRule rules new HashSet(); // 对 order-service 路由限流QPS 阈值 100 rules.add(new GatewayFlowRule(order-service) .setCount(100) .setIntervalSec(1) .setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT)); // 对 user-service 路由限流QPS 阈值 50 rules.add(new GatewayFlowRule(user-service) .setCount(50) .setIntervalSec(1)); GatewayRuleManager.loadRules(rules); } }setIntervalSec(1)表示统计窗口是 1 秒setCount(100)就是每秒 100 次。CONTROL_BEHAVIOR_DEFAULT是快速失败超过阈值直接拒绝还有WARM_UP预热和RATE_LIMITER匀速排队两种模式后面细说。3.4 规则持久化到 Nacos 的完整链路上面那段代码规则是写死在内存的重启就丢。生产必须走 Nacos 数据源。做法是实现GatewayRuleManager的数据源绑定Configuration public class SentinelNacosConfig { Value(${spring.cloud.nacos.config.server-addr}) private String nacosAddr; PostConstruct public void loadRules() { // 网关流控规则数据源 GatewayRuleManager.register2Property( new NacosDataSource(nacosAddr, DEFAULT_GROUP, gateway-flow-rules, source - JSON.parseObject(source, new TypeReferenceSetGatewayFlowRule() {})) .getProperty() ); // 降级规则数据源 DegradeRuleManager.register2Property( new NacosDataSource(nacosAddr, DEFAULT_GROUP, gateway-degrade-rules, source - JSON.parseObject(source, new TypeReferenceListDegradeRule() {})) .getProperty() ); } }然后在 Nacos 控制台建两个配置gateway-flow-rules和gateway-degrade-rules内容就是规则的 JSON 数组。改完发布网关通过长轮询实时感知不用重启。这一步是整个方案动态化的灵魂我第一次配通后改阈值从改代码重启变成改配置秒生效效率提升非常明显。4. 限流降级的策略细节与参数调优4.1 三种流控行为怎么选Sentinel 网关限流有三种控制行为选错了体验会很差快速失败默认超阈值直接返回 429简单粗暴。适合对实时性要求高、允许部分请求失败的场景比如秒杀入口。预热WARM_UP系统冷启动时阈值从低逐渐升到设定值防止刚启动就被打满。适合有缓存预热、连接池初始化的服务。参数warmUpPeriodSec控制预热时长我一般设 10 到 30 秒。匀速排队RATE_LIMITER请求排队等待按固定间隔放行超过最大等待时间才拒绝。适合削峰填谷比如订单创建这种不能丢请求的场景。参数maxQueueingTimeMs控制最长排队时间设太大用户会等到超时我一般不超过 500ms。我的经验是入口路由用快速失败核心写接口用匀速排队新上线服务用预热。三种混着用比一刀切合理得多。4.2 降级规则的熔断参数限流是挡流量降级是保命。当后端服务响应变慢或异常率升高时熔断器打开直接返回兜底响应给后端喘息时间。网关场景的降级规则关键参数参数含义我的常用值grade熔断策略慢调用/异常比例/异常数慢调用比例count慢调用 RT 阈值ms800slowRatioThreshold慢调用比例阈值0.5timeWindow熔断时长秒10minRequestAmount最小请求数20解释一下count800表示响应超过 800ms 算慢调用slowRatioThreshold0.5表示慢调用占比超过 50% 就熔断timeWindow10表示熔断 10 秒后进入半开状态试探。minRequestAmount20是防止请求量太少时误判——只有 2 个请求里 1 个慢比例 50% 就熔断显然不合理所以设个最小样本量。4.3 自定义限流响应默认的限流响应是 Sentinel 自带的格式固定前端不好处理。我一般自定义一个统一响应体返回业务错误码而不是裸的 429Component public class CustomBlockHandler implements BlockRequestHandler { Override public MonoServerResponse handleRequest(ServerWebExchange exchange, Throwable t) { return ServerResponse.status(HttpStatus.TOO_MANY_REQUESTS) .contentType(MediaType.APPLICATION_JSON) .body(BodyInserters.fromValue( Map.of(code, 429, msg, 请求过于频繁请稍后再试))); } }然后在配置里注册这个 handler。这样前端拿到的是一致结构的 JSON不用为限流单独写一套解析逻辑。这个细节看着小但联调时能省不少沟通成本。5. 常见问题排查与避坑实录5.1 限流不生效的排查顺序这是被问最多的问题。我整理了一个排查顺序按这个走基本能定位资源名对不对网关限流资源名是路由 ID不是 URL。先确认GatewayFlowRule里的名字和routes[].id一致。适配包引没引必须是sentinel-spring-cloud-gateway-adapter引成普通 starter 不生效。规则加载时机PostConstruct里加载规则要在网关初始化之后太早会被覆盖。用 Nacos 数据源的话检查配置的 dataId 和 group 是否匹配。是否被全局过滤器绕过自定义的 GlobalFilter 如果 order 值比 Sentinel 过滤器小可能在限流前就短路了请求。5.2 规则持久化后不刷新的坑Nacos 数据源配好了改配置却不生效八成是这几个原因Nacos 配置的group和代码里写的不一致默认DEFAULT_GROUPJSON 格式不合法导致解析失败Sentinel 会静默忽略或者 Nacos 命名空间没对上。我的习惯是先在 Nacos 控制台确认配置内容能被正确解析再排查代码。另外NacosDataSource的监听是长轮询改完配置有几秒延迟是正常的别急着判定失败。5.3 压测时阈值和实际不符压测发现设了 100 QPS实际放行了 130 左右。原因通常是统计窗口和并发的关系——Sentinel 的滑动窗口统计有精度误差加上网关本身的多线程并发瞬时值会略超。这不是 bug是统计模型的特性。如果业务对精度要求极高可以把intervalSec调小或者用集群流控。另外压测工具本身的并发模型也会影响观测值用固定线程数压和用固定 RPS 压看到的曲线不一样别拿一种工具的结果下结论。5.4 降级后恢复慢的问题熔断时间设太长后端恢复了但网关还在拒绝请求。timeWindow设 10 秒是折中太短会导致后端还没缓过来又被压垮太长影响恢复速度。我的做法是结合监控看后端恢复曲线如果后端 RT 在 3 秒内能回落到正常timeWindow设 5 到 10 秒足够如果后端恢复慢宁可设长一点配合告警人工介入。6. 我在实际项目中的几点体会这套组合跑下来最深的体会是限流阈值不是拍脑袋定的是压测压出来的。我见过太多团队直接抄个 1000 QPS 上去结果要么形同虚设要么误杀正常流量。正确做法是先摸清单机容量再按实例数折算留 20% 到 30% 余量最后用真实流量灰度验证。另一个体会是规则要版本化管理。Nacos 里的限流规则本质是配置改错了影响面很大。我习惯把规则 JSON 也纳入代码仓库每次变更走评审Nacos 上只做发布。这样出问题能快速回滚也能追溯是谁在什么时候改了什么。最后分享一个小技巧网关限流规则可以按 API 分组而不是按路由配。Sentinel 支持ApiDefinition把多个 URL 归到一个 API 名下然后对这个 API 名限流。比如把所有/order/**下的写接口归为order-write组统一限流比逐个路由配省事口径也更统一。这个用法在路由数量多的时候特别香值得试试。