【架构实战】限流算法全解析:令牌桶/滑动窗口/漏桶
一、为什么需要限流限流是保护系统的第一道防线防止突发流量压垮系统保证核心业务可用实现公平的资源分配二、常见限流算法1. 固定窗口计数器publicclassFixedWindowRateLimiter{privatefinalintlimit;privatefinallongwindowSize;privatelongwindowStart;privateintcount;publicsynchronizedbooleantryAcquire(){longnowSystem.currentTimeMillis();if(now-windowStartwindowSize){windowStartnow;count0;}if(countlimit){count;returntrue;}returnfalse;}}问题窗口边界可能出现2倍流量2. 滑动窗口计数器publicclassSlidingWindowRateLimiter{privatefinalintlimit;privatefinallongwindowSize;privatefinalLinkedListLongtimestampsnewLinkedList();publicsynchronizedbooleantryAcquire(){longnowSystem.currentTimeMillis();longwindowStartnow-windowSize;// 移除过期的时间戳while(!timestamps.isEmpty()timestamps.peekFirst()windowStart){timestamps.pollFirst();}if(timestamps.size()limit){timestamps.addLast(now);returntrue;}returnfalse;}}3. 漏桶算法请求 → 桶队列 → 以固定速率流出publicclassLeakyBucketRateLimiter{privatefinalintcapacity;privatefinalintrate;// 每秒流出数量privateintwater;privatelonglastLeakTime;publicsynchronizedbooleantryAcquire(){longnowSystem.currentTimeMillis();// 先漏水longelapsednow-lastLeakTime;intleaked(int)(elapsed*rate/1000);waterMath.max(0,water-leaked);lastLeakTimenow;// 再加水if(watercapacity){water;returntrue;}returnfalse;}}特点平滑流量但无法应对突发4. 令牌桶算法以固定速率生成令牌 → 桶 → 请求获取令牌publicclassTokenBucketRateLimiter{privatefinalintcapacity;privatefinalintrate;privateinttokens;privatelonglastRefillTime;publicsynchronizedbooleantryAcquire(){longnowSystem.currentTimeMillis();// 补充令牌longelapsednow-lastRefillTime;intnewTokens(int)(elapsed*rate/1000);tokensMath.min(capacity,tokensnewTokens);lastRefillTimenow;// 消费令牌if(tokens0){tokens--;returntrue;}returnfalse;}}特点允许一定程度的突发三、算法对比算法突发处理实现复杂度适用场景固定窗口差简单简单限流滑动窗口中中精确限流漏桶无中流量整形令牌桶好中通用限流四、分布式限流Redis Lua实现-- 令牌桶限流Lua脚本localkeyKEYS[1]localcapacitytonumber(ARGV[1])localratetonumber(ARGV[2])localnowtonumber(ARGV[3])locallastRefillredis.call(hget,key,lastRefill)localtokensredis.call(hget,key,tokens)ifnotlastRefillthenlastRefillnow tokenscapacityendlocalelapsednow-tonumber(lastRefill)localnewTokensmath.floor(elapsed*rate/1000)tokensmath.min(capacity,tonumber(tokens)newTokens)iftokens1thentokenstokens-1redis.call(hset,key,tokens,tokens)redis.call(hset,key,lastRefill,now)return1elsereturn0endpublicclassRedisRateLimiter{privatefinalStringscript;publicbooleantryAcquire(Stringkey,intcapacity,intrate){LongresultredisTemplate.execute(newDefaultRedisScript(script,Long.class),Collections.singletonList(key),String.valueOf(capacity),String.valueOf(rate),String.valueOf(System.currentTimeMillis()));returnresult!nullresult1;}}五、网关层限流Nginx限流# 限制请求速率 limit_req_zone $binary_remote_addr zoneapi:10m rate10r/s; server { location /api/ { limit_req zoneapi burst20 nodelay; proxy_pass http://backend; } }Sentinel限流SentinelResource(valueapi,blockHandlerhandleBlock)publicResultapi(){returnResult.ok();}publicResulthandleBlock(BlockExceptione){returnResult.error(请求太频繁);}六、总结令牌桶最常用支持突发漏桶流量整形滑动窗口精确限流分布式Redis Lua思考题你的系统用的是什么限流算法个人观点仅供参考