1、前言
在一些對高併發請求有限制的系統或者功能裏,好比說秒殺活動,或者一些網站返回的當前用戶過多,請稍後嘗試。這些都是經過對同一時刻請求數量進行了限制,通常用做對後臺系統的保護,防止系統由於過大的流量衝擊而崩潰。對於系統崩潰帶來的後果,顯然仍是拒絕一部分請求更能被維護者所接受。
而在各類限流中,除了系統自身設計的帶鎖機制的計數器外,利用Redis實現顯然是一種既高效安全又便捷方便的方式。java
2、incr命令
Redis Incr 命令將 key 中儲存的數字值增一。
若是 key 不存在,那麼 key 的值會先被初始化爲 0 ,而後再執行 INCR 操做。
若是值包含錯誤的類型,或字符串類型的值不能表示爲數字,那麼返回一個錯誤。
本操做的值限制在 64 位(bit)有符號數字表示以內。
示例:web
127.0.0.1:6379> set num 10 OK 127.0.0.1:6379> incr num (integer) 11 127.0.0.1:6379> get num # 數字值在 Redis 中以字符串的形式保存 "11"
注意: 因爲redis並無一個明確的類型來表示整型數據,因此這個操做是一個字符串操做。
執行這個操做的時候,key對應存儲的字符串被解析爲10進制的64位有符號整型數據。
事實上,Redis 內部採用整數形式(Integer representation)來存儲對應的整數值,因此對該類字符串值其實是用整數保存,也就不存在存儲整數的字符串表示(String representation)所帶來的額外消耗。redis
3、使用場景
1.計數器
使用思路是:每次有相關操做的時候,就向Redis服務器發送一個incr命令。
例如這樣一個場景:咱們有一個web應用,咱們想記錄每一個用戶天天訪問這個網站的次數。
web應用只須要經過拼接用戶id和表明當前時間的字符串做爲key,每次用戶訪問這個頁面的時候對這個key執行一下incr命令。api
這個場景能夠有不少種擴展方法:
經過結合使用INCR和EXPIRE命令,能夠實現一個只記錄用戶在指定間隔時間內的訪問次數的計數器
客戶端能夠經過GETSET命令獲取當前計數器的值而且重置爲0
經過相似於DECR或者INCRBY等原子遞增/遞減的命令,能夠根據用戶的操做來增長或者減小某些值 好比在線遊戲,須要對用戶的遊戲分數進行實時控制,分數可能增長也可能減小。安全
2.限速器
限速器是一種能夠限制某些操做執行速率的特殊場景。
傳統的例子就是限制某個公共api的請求數目。
假設咱們要解決以下問題:限制某個api每秒每一個ip的請求次數不超過10次。
咱們能夠經過incr命令來實現兩種方法解決這個問題。服務器
4、流量控制之java實現
這裏咱們將在java中使用redis-incr的特性來構建一個1分鐘內只容許 請求100次的控制代碼,key表明在redis內存放的被控制的鍵值。
併發
public static boolean flowControl(String key){ //最大容許100 int max = 100; long total = 1L; try { if (jedisInstance.get(key) == null) { //jedisInstance是Jedis鏈接實例,可使單連接也可使用連接池獲取,實現方式請參考以前的blog內容 //若是redis目前沒有這個key,建立並賦予0,有效時間爲60s jedisInstance.setex(key, 60, "0"); } else { //獲取加1後的值 total = jedisInstance.incr(redisKey).longValue(); //Redis TTL命令以秒爲單位返回key的剩餘過時時間。當key不存在時,返回-2。當key存在但沒有設置剩餘生存時間時,返回-1。不然,以秒爲單位,返回key的剩餘生存時間。 if (jedisInstance.ttl(redisKey).longValue() == -1L) { //爲給定key設置生存時間,當key過時時(生存時間爲0),它會被自動刪除。 jedisInstance.expire(redisKey, 60); } } } catch (Exception e) { logger.error("流量控制組件:執行計數操做失敗,沒法執行計數"); } long keytotaltransations = max; //判斷是否已超過最大值,超過則返回false if (total > keytotaltransations) { return false; } return true; }