接口被恶意刷爆?先自查:限流策略有没有精准配置?

172 阅读5分钟

接口被恶意刷爆?先自查:限流策略有没有精准配置?

作为一名摸爬滚打八年的 Java 老开发,我踩过的坑能组个加强连。其中最让人头皮发麻的,莫过于凌晨三点被运维电话叫醒 ——“线上接口被刷爆了,服务集群快扛不住了!” 而每次排查到最后,十有八九是限流策略在 “摸鱼”。今天就从实战角度,聊聊 Java 开发中限流配置的那些门道,帮你避开我踩过的坑。

一、那些年我栽过的限流 “坑”

刚入行那会,我对限流的理解停留在 “加个计数器就行”。直到第一次遭遇流量攻击,才明白天真有多可怕。

那次是公司电商平台的秒杀接口,我用 Guava 的 RateLimiter 做了单机限流,配置了 “每秒 100QPS”。结果活动刚开始,某台服务器直接被突发流量冲垮。后来才发现,单机限流在集群部署下就是个摆设 —— 流量被负载均衡分发到不同节点,单节点的限流阈值根本拦不住整体的恶意请求。

还有一次更离谱,我给用户登录接口配了 IP 限流,以为万事大吉。没想到攻击者用了 IP 池切换,几千个不同 IP 轮流轰炸,我的限流规则形同虚设。更致命的是,我忘了给限流后的降级逻辑做容错,导致大量请求堆积在队列里,最终引发服务雪崩。

这些血的教训让我明白:限流不是简单加个组件,而是一套需要精准适配业务场景的 “防御体系”。

二、精准限流的 3 个核心原则

1. 选对限流粒度,拒绝 “一刀切”

限流的核心是 “区分对象”,不同场景要选不同的粒度:

  • 普通用户接口:按 IP 限流,防止单 IP 恶意请求,但要预留动态调整空间,避免误拦正常用户。
  • 认证接口:按用户 ID+Token 双重限流,既限制单用户的请求频率,也防止 Token 伪造攻击。
  • 核心业务接口:按接口维度 + 业务类型限流,比如秒杀接口单独设置严格阈值,普通查询接口可适当放宽。

我现在维护的系统中,会用 Redis+Lua 脚本实现多维度限流,通过 Key 的组合(如 “limit:ip:192.168.1.1”“limit:user:10086”)精准控制不同对象的请求频率。

2. 匹配限流算法,适配业务场景

不同的业务场景,需要搭配不同的限流算法,选不对就会适得其反:

  • 突发流量场景(如秒杀):用令牌桶算法,支持预分配令牌应对流量峰值,我常用 Sentinel 的令牌桶实现,配置 “突发流量阈值” 缓冲峰值压力。
  • 平稳流量场景(如普通查询):用计数器滑动窗口算法,避免固定窗口的临界问题,比如用 Redis 的 ZSet 实现滑动窗口,精准统计每秒请求数。
  • 下游依赖保护(如调用第三方接口):用漏桶算法,严格控制请求输出速率,防止下游服务被压垮。

这里要提醒一句:别盲目跟风用高端组件,比如简单场景下 Guava RateLimiter 就足够,复杂分布式场景再上 Sentinel 或 Spring Cloud Gateway 限流。

3. 做好降级兜底,避免 “二次雪崩”

限流不是目的,保障系统可用性才是。如果只限流不降级,大量被拦截的请求会堆积在网关或服务端,最终还是会导致服务不可用。

我的经验是:

  • 接口层降级:返回默认数据或友好提示,比如 “当前请求人数过多,请稍后再试”,用 Spring 的 @HystrixCommand 注解快速实现。
  • 网关层降级:对非核心接口直接返回缓存数据,核心接口走限流队列,用 Spring Cloud Gateway 的 fallback 机制配置。
  • 熔断兜底:当接口失败率超过阈值时,直接熔断停止请求,避免连锁反应,我一般用 Sentinel 的熔断规则,配置 “失败率 20%” 作为触发阈值。

三、Java 实战:基于 Sentinel 的精准限流配置

结合我目前的生产实践,给大家分享一套基于 Sentinel 的落地方案,这套配置已经稳定运行了 1 年多。

1. 依赖引入

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-core</artifactId>
    <version>1.8.6</version>
</dependency>
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-spring-webmvc-adapter</artifactId>
    <version>1.8.6</version>
</dependency>

2. 核心配置

@Configuration
public class SentinelConfig {
    @PostConstruct
    public void initFlowRules() {
        // 1. 秒杀接口限流:IP维度,每秒5QPS
        FlowRule ipRule = new FlowRule();
        ipRule.setResource("seckill接口");
        ipRule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        ipRule.setCount(5);
        ipRule.setLimitApp("ip");

        // 2. 用户查询接口限流:用户ID维度,每秒10QPS
        FlowRule userRule = new FlowRule();
        userRule.setResource("userQuery接口");
        userRule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        userRule.setCount(10);
        userRule.setLimitApp("user");

        // 3. 配置降级规则:失败率20%触发熔断
        DegradeRule degradeRule = new DegradeRule();
        degradeRule.setResource("seckill接口");
        degradeRule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO);
        degradeRule.setCount(0.2);
        degradeRule.setTimeWindow(10);

        FlowRuleManager.loadRules(Collections.singletonList(ipRule));
        FlowRuleManager.loadRules(Collections.singletonList(userRule));
        DegradeRuleManager.loadRules(Collections.singletonList(degradeRule));
    }
}

3. 关键优化点

  • 动态调整阈值:结合监控数据,通过 Sentinel Dashboard 实时调整 QPS 阈值,避免高峰期限流过严或过松。
  • 白名单配置:给内部服务、测试 IP 配置限流白名单,防止正常业务被拦截。
  • 日志监控:集成 ELK 收集限流日志,通过 “限流 IP”“限流接口” 维度分析攻击来源,提前做好防御。

四、八年老开发的限流心得

  1. 限流要 “循序渐进”:先监控接口的正常 QPS 峰值,再设置略高于峰值的限流阈值,后续根据实际情况微调。
  2. 避免 “过度限流”:有些团队为了安全设置过严的阈值,导致正常用户请求被拦截,反而影响业务。
  3. 定期做压力测试:每季度模拟流量攻击,检验限流策略的有效性,比如用 JMeter 模拟 10 万 QPS 请求,观察系统是否能稳定应对。

最后想说,限流不是 “一劳永逸” 的事,它需要随着业务增长和攻击手段升级不断优化。作为后端开发,我们既要懂技术实现,更要站在业务和安全的角度思考问题,才能真正守住系统的 “第一道防线”。