我接手过一个做支付通道的系统,每天早高峰并发 5 万 QPS,3 台 4 核机器稳如老狗。
然后某天接了个 618 大促的合作方,单接口峰值冲到 2 万 QPS,比平时整个系统加起来还多。10 分钟后数据库连接池打满,全站崩溃。
事后复盘,限流组件是有的,但配置写的是"全局 1000 QPS"。合作方一秒钟打 5000 进来,限流直接把自己的正常用户也限了。
这就是没真正理解限流算法背后设计模式的代价。你把 Sentinel 拉进来用,不代表你懂限流。本文拆给你看。
限流的 3 种核心算法,本质是 3 种策略模式
1. 令牌桶(Token Bucket)
想象有个桶,每隔一段时间往里放 N 个令牌。每个请求来都要从桶里拿一个令牌,没有就拒绝。
```java public class TokenBucket { private final long capacity; // 桶容量 private final double refillRate; // 每秒补充令牌数 private double tokens; // 当前令牌数 private long lastRefillTimestamp; // 上次补充时间
public synchronized boolean tryAcquire() { refill(); if (tokens >= 1) { tokens -= 1; return true; } return false; } private void refill() { long now = System.currentTimeMillis(); double tokensToAdd = (now - lastRefillTimestamp) / 1000.0 * refillRate; tokens = Math.min(capacity, tokens + tokensToAdd); lastRefillTimestamp = now; }} ```
特点:允许"突发流量"。比如桶容量 100,每秒补充 10 个,平时没流量桶是满的,突然 100 个请求一起过来能全放行。
真实坑:很多教程告诉你令牌桶适合"突发流量"就完事了。但生产环境你要考虑令牌生成和消费的时序问题——多个线程同时tryAcquire时,必须加锁或用 CAS。我见过一个版本没加锁,并发 5 万 QPS 时令牌数变负数,限流失效。