共計 3786 個字符,預計需要花費 10 分鐘才能閱讀完成。
這篇文章主要介紹了 Redis 實現限流器的方法有哪些的相關知識,內容詳細易懂,操作簡單快捷,具有一定借鑒價值,相信大家閱讀完這篇 Redis 實現限流器的方法有哪些文章都會有所收獲,下面我們一起來看看吧。
方法一:基于 Redis 的 setnx 的操作
我們在使用 Redis 的分布式鎖的時候,大家都知道是依靠了 setnx 的指令,在 CAS(Compare and swap)的操作的時候,同時給指定的 key 設置了過期實踐(expire),我們在限流的主要目的就是為了在單位時間內,有且僅有 N 數量的請求能夠訪問我的代碼程序。所以依靠 setnx 可以很輕松的做到這方面的功能。
比如我們需要在 10 秒內限定 20 個請求,那么我們在 setnx 的時候可以設置過期時間 10,當請求的 setnx 數量達到 20 時候即達到了限流效果。代碼比較簡單就不做展示了。
當然這種做法的弊端是很多的,比如當統計 1 -10 秒的時候,無法統計 2 -11 秒之內,如果需要統計 N 秒內的 M 個請求,那么我們的 Redis 中需要保持 N 個 key 等等問題。
在具體實現的時候,可以考慮使用攔截器 HandlerInterceptor:
public class RequestCountInterceptor implements HandlerInterceptor {
private LimitPolicy limitPolicy;
public RequestCountInterceptor(LimitPolicy limitPolicy) {
this.limitPolicy = limitPolicy;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (!limitPolicy.canDo()) {
return false;
}
return true;
}
}
同時添加一個配置 LimitConfiguration:
@Configuration
public class LimitConfiguration implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new RequestCountInterceptor(new RedisLimit1())).addPathPatterns( /my/increase
}
}
這樣每次在 /my/increase 請求到達 Controller 之前按策略 RedisLimit1 進行限流,原先 Controller 里面的代碼就不用修改了:
@RestController
@RequestMapping(my)
public class MyController {
int i = 0;
@RequestMapping(/increase)
public int increase() {
return i++;
}
}
具體的限流邏輯代碼是在 RedisLimit1 類中:
/**
* 方法一:基于 Redis 的 setnx 的操作
public class RedisLimit1 extends LimitPolicy {
static { setNxExpire();
}
private static boolean setNxExpire() { SetParams setParams = new SetParams();
setParams.nx();
setParams.px(TIME);
String result = jedis.set(KEY, COUNT + , setParams);
// 設置失敗,說明已經存在,直接減 1,并且返回
return jedis.decrBy(KEY, 1) 0;
}
}
public abstract class LimitPolicy {
public static final int COUNT = 10; //10 request
public static final int TIME= 10*1000 ; // 10s
public static final String SUCCESS = OK
static Jedis jedis = new Jedis();
abstract boolean canDo();}
這樣實現的一個效果是每秒最多請求 10 次。
方法二:基于 Redis 的數據結構 zset
其實限流涉及的最主要的就是滑動窗口,上面也提到 1 -10 怎么變成 2 -11。其實也就是起始值和末端值都各 + 1 即可。
而我們如果用 Redis 的 list 數據結構可以輕而易舉的實現該功能
我們可以將請求打造成一個 zset 數組,當每一次請求進來的時候,value 保持唯一,可以用 UUID 生成,而 score 可以用當前時間戳表示,因為 score 我們可以用來計算當前時間戳之內有多少的請求數量。而 zset 數據結構也提供了 zrange 方法讓我們可以很輕易的獲取到 2 個時間戳內有多少請求
/**
* 方法二:基于 Redis 的數據結構 zset
public class RedisLimit2 extends LimitPolicy {
public static final String KEY2 = LIMIT2
@Override
public boolean canDo() { Long currentTime = new Date().getTime();
System.out.println(currentTime);
if (jedis.zcard(KEY2) 0) { // 這里不能用 get 判斷,會報錯:WRONGTYPE Operation against a key holding the wrong kind of value
Integer count = jedis.zrangeByScore(KEY2, currentTime - TIME, currentTime).size(); // 注意這里使用 zrangeByScore,以時間作為 score。zrange key start stop 命令的 start 和 stop 是序號。 System.out.println(count);
if (count != null count COUNT) {
return false;
}
}
jedis.zadd(KEY2, Double.valueOf(currentTime), UUID.randomUUID().toString());
return true;
}
}
通過上述代碼可以做到滑動窗口的效果,并且能保證每 N 秒內至多 M 個請求,缺點就是 zset 的數據結構會越來越大。實現方式相對也是比較簡單的。
方法三:基于 Redis 的令牌桶算法
提到限流就不得不提到令牌桶算法了。令牌桶算法提及到輸入速率和輸出速率,當輸出速率大于輸入速率,那么就是超出流量限制了。也就是說我們每訪問一次請求的時候,可以從 Redis 中獲取一個令牌,如果拿到令牌了,那就說明沒超出限制,而如果拿不到,則結果相反。
依靠上述的思想,我們可以結合 Redis 的 List 數據結構很輕易的做到這樣的代碼,只是簡單實現 依靠 List 的 leftPop 來獲取令牌。
首先配置一個定時任務,通過 redis 的 list 的 rpush 方法每秒插入一個令牌:
@Configuration //1. 主要用于標記配置類,兼備 Component 的效果。@EnableScheduling // 2. 開啟定時任務
public class SaticScheduleTask {
//3. 添加定時任務
@Scheduled(fixedRate = 1000)
private void configureTasks() { LimitPolicy.jedis.rpush( LIMIT3 , UUID.randomUUID().toString());
}
}
限流時,通過 list 的 lpop 方法從 redis 中獲取對應的令牌,如果獲取成功表明可以執行請求:
/**
* 方法三:令牌桶
public class RedisLimit3 extends LimitPolicy {
public static final String KEY3 = LIMIT3
@Override
public boolean canDo() { Object result = jedis.lpop(KEY3);
if (result == null) {
return false;
}
return true;
}
}
關于“Redis 實現限流器的方法有哪些”這篇文章的內容就介紹到這里,感謝各位的閱讀!相信大家對“Redis 實現限流器的方法有哪些”知識都有一定的了解,大家如果還想學習更多知識,歡迎關注丸趣 TV 行業資訊頻道。