
1. 为什么我们需要API限流上周我们系统突然遭遇了一次流量激增某个新上线的接口被疯狂调用直接导致整个服务雪崩。事后排查发现原来是有个合作方在测试环境误把循环调用逻辑配成了生产环境。这种突发流量如果没做好防护分分钟就能让系统挂掉。这就是为什么今天要跟大家聊聊SpringBoot下的API限流实战。API限流本质上是一种保护机制就像给水库安装泄洪闸一样。当上游水量过大时通过控制闸门开合来保证下游安全。在微服务架构中常见的限流场景包括防止恶意刷接口避免内部服务调用超载应对突发流量冲击保证核心业务优先级2. 主流限流方案选型对比2.1 令牌桶 vs 漏桶算法最近在技术社区看到很多关于限流算法的讨论这里用个外卖骑手的例子来解释令牌桶就像餐厅出餐口厨师匀速做菜生成令牌骑手取到餐令牌才能配送。允许短时爆发取多个令牌但长期平均速率固定。漏桶更像外卖平台的派单系统不管骑手接单多快平台都按固定速率派单漏水速率恒定。我们项目最终选择了令牌桶因为更适合突发流量场景如秒杀活动实现简单且扩展性强与Spring生态集成度更好2.2 单机限流 vs 分布式限流在技术方案评审时架构师坚持要用Redis做分布式限流。这里分享我们的决策过程方案类型适用场景实现复杂度性能影响一致性单机限流内部服务调用低1ms弱Redis限流对外API网关中3-5ms强Sentinel集群全链路控制高2-3ms最终对于后台管理系统这类内部服务用Guava的RateLimiter就够了。而面向C端的开放平台接口就必须上RedisLua脚本的方案。3. SpringBoot集成实战3.1 基于AOP的注解式限流先看我们项目中正在用的方案自定义RateLimit注解。这种声明式的方式对业务代码零侵入特别适合老项目改造。Target(ElementType.METHOD) Retention(RetentionPolicy.RUNTIME) public interface RateLimit { String key() default ; int permits() default 10; int time() default 60; }切面核心逻辑用了Guava的RateLimiterAround(annotation(rateLimit)) public Object around(ProceedingJoinPoint point, RateLimit rateLimit) throws Throwable { String key getCacheKey(point, rateLimit); RateLimiter limiter limiters.computeIfAbsent(key, k - RateLimiter.create(rateLimit.permits() / (double)rateLimit.time())); if (!limiter.tryAcquire()) { throw new BusinessException(429, 手速太快啦稍后再试); } return point.proceed(); }踩坑提醒RateLimiter.create()的参数是每秒许可数我们第一次就有人把分钟数直接当秒数传导致限流失效。3.2 RedisLua分布式方案对于需要集群限流的场景我们基于Redis实现了这个方案。先看Lua脚本local key KEYS[1] local limit tonumber(ARGV[1]) local expire tonumber(ARGV[2]) local current tonumber(redis.call(get, key) or 0) if current 1 limit then return 0 else redis.call(INCR, key) if current 0 then redis.call(EXPIRE, key, expire) end return 1 endSpring中通过RedisTemplate调用public boolean tryAcquire(String key, int limit, int expire) { ListString keys Collections.singletonList(key); DefaultRedisScriptLong script new DefaultRedisScript(LUA_SCRIPT, Long.class); return 1L.equals(redisTemplate.execute(script, keys, String.valueOf(limit), String.valueOf(expire))); }这个方案的性能实测单节点QPS可达8000集群环境下误差3%网络延迟是主要瓶颈4. 生产环境调优经验4.1 动态限流配置我们在Nacos里放了这样的配置rateLimit: rules: - resource: /api/order/create count: 100 period: 10 - resource: /api/payment/callback count: 500 period: 1通过监听配置变更实现热更新RefreshScope public class RateLimitConfig { NacosValue(${rateLimit.rules}) private String rules; // 解析规则到内存Map }4.2 分级降级策略当系统负载过高时我们实施三级降级非核心接口限流如数据看板延迟敏感接口降级同步改异步熔断保护核心链路如支付对应的Hystrix配置示例HystrixCommand( fallbackMethod fallbackHandler, commandProperties { HystrixProperty(namecircuitBreaker.requestVolumeThreshold, value20), HystrixProperty(namecircuitBreaker.sleepWindowInMilliseconds, value5000) } )5. 监控与问题排查5.1 Prometheus监控指标我们在 actuator 中暴露了这些关键指标rate_limit_requests_total总请求数rate_limit_rejected_total被拒请求数rate_limit_remaining_permits剩余许可数Grafana看板配置示例sum(rate(rate_limit_rejected_total{application$application}[1m])) by (resource)5.2 常见问题排查指南最近线上遇到的几个典型问题限流不生效检查时间单位秒/分钟混淆验证Redis连接是否正常确认AOP切点表达式匹配性能瓶颈Redis pipeline批量处理本地缓存热点key适当调大连接池突发流量处理预热RateLimiter调用acquire()初始化设置burst size允许的突发量结合消息队列削峰6. 进阶优化方向对于高并发场景我们正在测试这些优化方案使用Redisson的RRateLimiter尝试Sentinel的集群流控基于机器负载动态调整限流阈值有个特别实用的技巧在网关层做全局限流后再到业务层做细粒度控制形成双层防护。实测下来这种方案能减少30%的不必要请求穿透。