线上网关一抖,下游服务跟着陪葬。做限流,核心从来不是“把请求拦在外面”,而是保核心链路、削峰填谷、给系统留口气。面对爬虫脚本、黑产刷单、或者大促前突然冲进来的流量,靠静态阈值和 Redis 计数器硬扛,基本就是给运维埋雷。
这篇不扯概念,直接聊 Spring Cloud Gateway 怎么结合 Sentinel 把限流、动态调参、智能封禁落地到生产环境。中间会穿插几个我们团队实际踩过的坑,代码和配置都经过线上验证,可以直接抄作业。
一、 为什么网关限流总翻车?Sentinel 到底强在哪
很多团队一开始用 Redis + Lua 写个滑动窗口,或者在 Nginx 配个limit_req,平时看着挺美,一遇到复杂场景就露馅:规则改一次要重启,IP 和维度耦合死板,下游服务 RT 飙高了它还在傻傻放行。
Sentinel 能解决这些问题,靠的不是什么魔法,而是几个实打实的特性:
- 网关原生支持:
spring-cloud-alibaba-sentinel-gateway提供了GatewayFlowRule,专门针对路由、API 分组做限流,不用你自己去解析路由树。 - 维度拆解灵活:不仅能按 QPS 或线程数限,还能按 IP、Header、URL 参数、Cookie 做热点限流。爬虫换 IP 或者多账号并发,用参数维度能直接打穿它的伪装。
- 规则热更新:接上 Nacos 或 Apollo,配置改完秒级推送到各网关节点。线上调阈值不用发版,半夜被叫起来也不用慌。
- 开销确实低:底层用
LeapArray做滑动窗口统计,纯内存操作。单节点扛个十万级 QPS 问题不大,CPU 和内存损耗都在可接受范围内,别信那些动辄“百万级”的夸张说法,网关本身还有序列化、路由转发的开销。
二、 架构怎么搭才不踩坑
生产环境的限流防刷,得走“采集 → 决策 → 执行 → 反馈”的闭环。别把逻辑全塞在网关节点里,网关只负责快速拦截和路由,决策和封禁状态交给外部存储。
[外部流量] -> [CDN/WAF] -> [Spring Cloud Gateway 集群] ↓ (Sentinel Gateway Filter 拦截) [指标采集] (QPS/RT/拦截率/下游异常率) ↓ [规则引擎/配置中心] (动态阈值/策略下发) ↓ [热点参数限流] <---> [封禁服务] (Redis 黑名单/渐进策略/行为画像) ↓ [下游微服务集群]几个关键节点说明:
- 采集层:Sentinel 内部按秒/分钟窗口统计通过率、平均 RT、被拦截数。网关层通过
RequestOriginParser提取请求来源标识(通常是 IP 或设备指纹),后续限流和封禁都基于这个标识打点。 - 决策层:静态阈值死板,生产上得结合系统负载做自适应。Sentinel 的
SystemRule会看 Load、CPU、平均 RT 和并发线程数,负载高了自动收紧入口流量,算法底层借鉴了 BBR 拥塞控制的思想,比简单的固定阈值抗揍得多。 - 执行与封禁:限流是第一步,封禁是第二步。别一触发阈值就永久封 IP,得搞渐进式:第一次 429 警告,第二次临时封 15 分钟,三次以上进 Redis 黑名单(带 TTL)。对可疑但又不确定的请求,可以直接注入延迟或返回脱敏数据(影子封禁),既消耗爬虫算力,又避免误杀真实用户。
三、 核心代码落地(附常见错误修正)
1. 依赖引入
注意版本号要对齐你们项目的 Spring Cloud Alibaba 版本,别乱混用。
<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.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId></dependency>2. 规则动态推送(Nacos)
配置中心推的是 JSON,网关侧通过DataSource监听变更。注意rule-type: gw-flow别写错,写错直接解析失败。
spring:cloud:sentinel:transport:dashboard:localhost:8080port:8719datasource:gw-flow:nacos:server-addr:${NACOS_ADDR:localhost:8848}data-id:sentinel-gateway-rulesgroup-id:DEFAULT_GROUPrule-type:gw_flowdata-type:jsonNacos 里的配置长这样(针对热点参数限流):
[{"resource":"api-order-create","count":500,"intervalSec":1,"paramItem":{"parseStrategy":0,"fieldName":null,"pattern":null,"matchStrategy":0}}]parseStrategy: 0代表按客户端 IP 限流。这里提个醒,GatewayFlowRule和普通的ParamFlowRule是两套东西,后者是给@SentinelResource方法级限流用的,网关层必须用GatewayFlowRule,否则规则根本加载不进去。
3. 来源解析与自定义拦截
网关是响应式的,提取 IP 时注意拿全代理头。
@ConfigurationpublicclassSentinelGatewayConfig{@PostConstructpublicvoidinit(){SentinelGatewayFilter.setRequestOriginParser(request->{Stringip=request.getHeaders().getFirst("X-Real-IP");if(StringUtils.isBlank(ip)){ip=request.getHeaders().getFirst("X-Forwarded-For");}returnStringUtils.isNotBlank(ip)?ip:"unknown";});}}拦截器处理被限流请求。WebFlux 环境下写响应体得小心,直接writeWith就行,别用阻塞的 IO。
@ComponentpublicclassCustomBlockExceptionHandlerimplementsBlockExceptionHandler{privatefinalBanEvaluationServicebanService;publicCustomBlockExceptionHandler(BanEvaluationServicebanService){this.banService=banService;}@Overridepublicvoidhandle(ServerHttpRequestrequest,ServerHttpResponseresponse,BlockExceptione)throwsException{response.setStatusCode(HttpStatus.TOO_MANY_REQUESTS);response.getHeaders().setContentType(MediaType.APPLICATION_JSON);Stringorigin=request.getHeaders().getFirst("X-Real-IP");Stringresource=e.getResourceName();// 异步提交封禁评估,别阻塞网关线程banService.asyncEvaluate(origin,resource,e.getClass().getSimpleName());Stringbody="{\"code\":42901,\"msg\":\"请求过于频繁,请稍后重试\"}";DataBufferbuffer=response.bufferFactory().wrap(body.getBytes(StandardCharsets.UTF_8));response.writeWith(Mono.just(buffer)).subscribe();}}四、 生产环境的“暗礁”:规则同步、误封与一致性
网关集群部署后,规则一致性是个老大难。Sentinel 默认是单节点内存生效,如果不靠配置中心强推,各节点规则很容易跑偏。
规则漂移怎么防?
配置中心推送必须带版本号。本地内存规则定期和 Nacos 拉取的最新版本做 Diff,偏差超过 5% 或者连续 N 次心跳未同步,直接触发全量重载。配置中心挂了怎么办?网关节点必须硬编码一套fallback阈值(比如核心接口 100 QPS),保底不断链。
误封怎么兜底?
动态封禁最怕把内部系统、CDN 回源 IP 或者正常搞活动的运营账号给封了。上线前先拉白名单列表,走网关节点直接跳过限流链。对于疑似误封的 IP,别一刀切,先走“影子放行”:正常透传请求,但打上标记日志,跑几天行为模型看是不是真人。真误封了,给业务侧开个自助申诉接口,后台调 Redis 缩短黑名单 TTL 就行。
跨节点怎么同步?
Sentinel 本身不维护全局状态,黑名单、计数器全扔 Redis。规则变更时,通过 MQ 广播个事件,各节点监听后本地刷新内存。限流本来就是概率性防守,允许短暂窗口内有几毫秒的误差,别为了强一致性把网关拖慢,最终一致性在网关场景完全够用。
五、 监控告警别搞成“狼来了”
Prometheus + Grafana 是标配,暴露 Sentinel 指标:
management:endpoints:web:exposure:include:prometheus,sentinelmetrics:tags:application:${spring.application.name}看板盯几个核心指标就行:sentinel_qps、sentinel_block_qps、sentinel_rt、网关自定义的ban_ip_count。
告警策略别搞得太敏感,否则运维手机响个不停:
- P1(服务级):拦截率连续 2 分钟超过 60%,且下游 RT 飙升。直接电话+企微,网关自动切降级页或排队页。
- P2(规则级):单节点规则同步失败、Nacos 断连。发运维群,值班人员介入。
- P3(风控级):单一 IP 或参数维度 QPS 突增 300%。不告警,直接扔给风控工单系统,自动分析特征。
阈值回滚也得自动化。如果封禁策略上线后,核心业务指标(如下单转化率、登录成功率)断崖下跌,说明策略太狠。写个定时任务,对比历史健康基线,自动把阈值回调 30%~50%,等业务平稳再慢慢收紧。
六、 线上真实案例复盘
案例 1:代理池爬虫刷商品库存
现象:竞品搞了个分布式代理池,每秒换几十个 IP 调/api/stock/query,URL 里还带随机时间戳,静态 IP 限流形同虚设。
怎么解决的:
- 网关层把限流维度换成
clientIp + 设备指纹Header,阈值压到 25 QPS。 - 封禁服务发现 10 分钟内 5 次超限,自动写 Redis 黑名单 2 小时。
- 对黑名单 IP 不走拦截,而是自定义 Filter 直接返回固定库存
99。爬虫拿到假数据继续跑,白白消耗算力。 - 事后抓包发现请求参数熵值极低,风控模型加上“参数随机性校验”,次日拦截率直接拉到 99% 以上。
案例 2:大促前流量突刺
现象:活动预告发出去,网关 QPS 从 3k 瞬间飙到 80k,订单服务线程池打满,数据库连接池报警。
怎么扛的:
SystemRule检测到 Load 突破阈值,自动收紧入口流量,拒绝多余请求。- 网关直接返回
202 Accepted带Retry-After头,前端按指数退避重试,把瞬时尖峰拉平成缓坡。 - Sentinel 根据下游平均 RT 动态下调限流阈值,避免瞬间拒绝导致客户端疯狂重试的雪崩。
- 活动结束,阈值按指数衰减恢复。全程订单服务没 OOM,数据库连接数一直压在安全水位线内。
七、 几句掏心窝子的建议
限流防刷不是配几条规则就完事了,是个持续对抗的过程。一线实战下来,记住这几条能少熬几个大夜:
- 先限后封,留足缓冲:封 IP 是最后手段。先用排队、降级、Mock 响应消化压力,直接硬拦容易引发客户端重试风暴。
- 动态阈值是底线:固定阈值上线三个月必失效。系统负载、业务周期、爬虫策略都在变,阈值必须跟着指标联动。
- 维度交叉打组合拳:单靠 IP 限流早过时了。IP + UserId + 设备指纹 + 行为熵值 + 地理位置,维度越多,绕过成本越高。
- 规则必须版本化:所有限流策略进 Git 或配置中心版本库。上线出问题,一键回滚比临时排查快得多。
- 别指望单点防御:CDN 缓存 + WAF + 网关限流 + 服务降级 + DB 连接池保护,层层递进。Sentinel 很强,但不是银弹。
- 预发必须做压测和误封演练:阈值怎么来的?压测打出来的。封禁策略会不会误杀正常用户?在预发环境拿测试账号跑一遍就知道。
流量治理没有一劳永逸的架构,只有不断迭代的策略。把 Sentinel 的能力揉进业务上下文,配好监控和自动化兜底,系统才能在各种突发流量里稳住阵脚。
🎁 福利时间
如果你正在备战面试或者想要学习其他知识,给大家推荐一个宝藏知识库,作者整理了一些列 Java 程序员需要掌握的核心知识,有需要的自取不谢。
知识库地址:https://farerboy.com/