Redis实现分布式限流的几种方法

张开发
2026/5/13 10:40:31 15 分钟阅读
Redis实现分布式限流的几种方法
使用Redis实现分布式限流是一种常见且有效的方法可以防止系统过载并确保公平的资源分配。Redis的高性能和丰富的数据结构使其成为实现分布式限流的理想选择。常见的限流算法包括固定窗口计数、滑动窗口计数和令牌桶算法。1. 固定窗口计数算法固定窗口计数算法将时间划分为固定长度的窗口并在每个窗口内计数请求的数量。示例代码以下示例展示了如何使用Redis实现固定窗口计数算法的分布式限流1234567891011121314151617181920212223242526272829303132333435363738394041424344454647importredis.clients.jedis.Jedis;publicclassFixedWindowRateLimiter {privateJedis jedis;privateintmaxRequests;privateintwindowSize;// 窗口大小单位为秒publicFixedWindowRateLimiter(String host,intport,intmaxRequests,intwindowSize) {this.jedis newJedis(host, port);this.maxRequests maxRequests;this.windowSize windowSize;}publicbooleanisAllowed(String clientId) {String key rate_limiter: clientId;longcurrentWindow System.currentTimeMillis() /1000/ windowSize;String windowKey key : currentWindow;longrequestCount jedis.incr(windowKey);if(requestCount 1) {jedis.expire(windowKey, windowSize);}returnrequestCount maxRequests;}publicvoidclose() {jedis.close();}publicstaticvoidmain(String[] args) {FixedWindowRateLimiter rateLimiter newFixedWindowRateLimiter(localhost,6379,5,60);for(inti 0; i 10; i) {booleanallowed rateLimiter.isAllowed(client1);System.out.println(Request (i 1) allowed: allowed);try{Thread.sleep(500);// 模拟请求间隔}catch(InterruptedException e) {e.printStackTrace();}}rateLimiter.close();}}2. 滑动窗口计数算法滑动窗口计数算法通过记录多个小窗口内的请求数计算滑动窗口内的总请求数。示例代码以下示例展示了如何使用Redis实现滑动窗口计数算法的分布式限流1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556importredis.clients.jedis.Jedis;importredis.clients.jedis.Transaction;importjava.util.List;publicclassSlidingWindowRateLimiter {privateJedis jedis;privateintmaxRequests;privateintwindowSize;// 窗口大小单位为秒privateintinterval;// 时间间隔单位为秒publicSlidingWindowRateLimiter(String host,intport,intmaxRequests,intwindowSize,intinterval) {this.jedis newJedis(host, port);this.maxRequests maxRequests;this.windowSize windowSize;this.interval interval;}publicbooleanisAllowed(String clientId) {String key rate_limiter: clientId;longcurrentTime System.currentTimeMillis() /1000;longwindowStart currentTime - windowSize;Transaction transaction jedis.multi();transaction.zadd(key, currentTime, String.valueOf(currentTime));transaction.zremrangeByScore(key,0, windowStart);transaction.zcard(key);transaction.expire(key, windowSize interval);ListObject results transaction.exec();longrequestCount (long) results.get(2);returnrequestCount maxRequests;}publicvoidclose() {jedis.close();}publicstaticvoidmain(String[] args) {SlidingWindowRateLimiter rateLimiter newSlidingWindowRateLimiter(localhost,6379,5,60,1);for(inti 0; i 10; i) {booleanallowed rateLimiter.isAllowed(client1);System.out.println(Request (i 1) allowed: allowed);try{Thread.sleep(500);// 模拟请求间隔}catch(InterruptedException e) {e.printStackTrace();}}rateLimiter.close();}}3. 令牌桶算法令牌桶算法通过生成令牌来控制请求的速率。每次请求需要消耗一个令牌如果桶中没有令牌则请求被拒绝。示例代码以下示例展示了如何使用Redis实现令牌桶算法的分布式限流123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657importredis.clients.jedis.Jedis;publicclassTokenBucketRateLimiter {privateJedis jedis;privateintmaxTokens;privateintrefillRate;// 令牌生成速率单位为令牌/秒publicTokenBucketRateLimiter(String host,intport,intmaxTokens,intrefillRate) {this.jedis newJedis(host, port);this.maxTokens maxTokens;this.refillRate refillRate;}publicbooleanisAllowed(String clientId) {String key rate_limiter: clientId;longcurrentTime System.currentTimeMillis() /1000;longlastRefillTime jedis.hget(key,lastRefillTime) null?0: Long.parseLong(jedis.hget(key,lastRefillTime));inttokens jedis.hget(key,tokens) null?maxTokens : Integer.parseInt(jedis.hget(key,tokens));longtokensToAdd (currentTime - lastRefillTime) * refillRate;tokens Math.min(maxTokens, tokens (int) tokensToAdd);lastRefillTime currentTime;if(tokens 0) {jedis.hset(key,tokens, String.valueOf(tokens -1));jedis.hset(key,lastRefillTime, String.valueOf(lastRefillTime));returntrue;}else{jedis.hset(key,tokens, String.valueOf(tokens));jedis.hset(key,lastRefillTime, String.valueOf(lastRefillTime));returnfalse;}}publicvoidclose() {jedis.close();}publicstaticvoidmain(String[] args) {TokenBucketRateLimiter rateLimiter newTokenBucketRateLimiter(localhost,6379,5,1);for(inti 0; i 10; i) {booleanallowed rateLimiter.isAllowed(client1);System.out.println(Request (i 1) allowed: allowed);try{Thread.sleep(500);// 模拟请求间隔}catch(InterruptedException e) {e.printStackTrace();}}rateLimiter.close();}}4. 令牌桶算法与Lua脚本为了确保限流操作的原子性可以使用Redis的Lua脚本。以下示例展示了如何结合Lua脚本和令牌桶算法来实现分布式限流。Lua脚本保存为token_bucket.lua123456789101112131415161718192021local key KEYS[1]local maxTokens tonumber(ARGV[1])local refillRate tonumber(ARGV[2])local currentTime tonumber(ARGV[3])local tokens tonumber(redis.call(hget, key, tokens) or maxTokens)local lastRefillTime tonumber(redis.call(hget, key, lastRefillTime) or 0)local tokensToAdd math.floor((currentTime - lastRefillTime) * refillRate)tokens math.min(maxTokens, tokens tokensToAdd)lastRefillTime currentTimeif tokens 0 thenredis.call(hset, key, tokens, tokens - 1)redis.call(hset, key, lastRefillTime, lastRefillTime)return 1elseredis.call(hset, key, tokens, tokens)redis.call(hset, key, lastRefillTime, lastRefillTime)return 0endJava代码123456789101112131415161718192021222324importredis.clients.jedis.Jedis;importredis.clients.jedis.JedisPool;importjava.io.IOException;importjava.nio.file.Files;importjava.nio.file.Paths;publicclassTokenBucketRateLimiterWithLua {privateJedisPool jedisPool;privateString luaScript;privateString scriptSha;publicTokenBucketRateLimiterWithLua(String host,intport, String scriptPath)throwsIOException {this.jedisPool newJedisPool(host, port);this.luaScript newString(Files.readAllBytes(Paths.get(scriptPath)));try(Jedis jedis jedisPool.getResource()) {this.scriptSha jedis.scriptLoad(luaScript);}}publicbooleanisAllowed(String clientId,intmaxTokens,intrefillRate) {String key rate_limiter: clientId;longcurrentTime System.currentTimeMillis() /1000;到此这篇关于Redis实现分布式限流的几种方法 的文章就介绍到这了,

更多文章