Redis原子計(jì)數(shù)器incr,防止并發(fā)請求操作
一、前言
在一些對高并發(fā)請求有限制的系統(tǒng)或者功能里,比如說秒殺活動,或者一些網(wǎng)站返回的當(dāng)前用戶過多,請稍后嘗試。這些都是通過對同一時刻請求數(shù)量進(jìn)行了限制,一般用作對后臺系統(tǒng)的保護(hù),防止系統(tǒng)因?yàn)檫^大的流量沖擊而崩潰。對于系統(tǒng)崩潰帶來的后果,顯然還是拒絕一部分請求更能被維護(hù)者所接受。
而在各種限流中,除了系統(tǒng)自身設(shè)計(jì)的帶鎖機(jī)制的計(jì)數(shù)器外,利用Redis實(shí)現(xiàn)顯然是一種既高效安全又便捷方便的方式。
二、incr命令
Redis Incr 命令將 key 中儲存的數(shù)字值增一。
如果 key 不存在,那么 key 的值會先被初始化為 0 ,然后再執(zhí)行 INCR 操作。
如果值包含錯誤的類型,或字符串類型的值不能表示為數(shù)字,那么返回一個錯誤。
本操作的值限制在 64 位(bit)有符號數(shù)字表示之內(nèi)。
示例:
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 # 數(shù)字值在 Redis 中以字符串的形式保存 "11"
注意: 由于redis并沒有一個明確的類型來表示整型數(shù)據(jù),所以這個操作是一個字符串操作。
執(zhí)行這個操作的時候,key對應(yīng)存儲的字符串被解析為10進(jìn)制的64位有符號整型數(shù)據(jù)。
事實(shí)上,Redis 內(nèi)部采用整數(shù)形式(Integer representation)來存儲對應(yīng)的整數(shù)值,所以對該類字符串值實(shí)際上是用整數(shù)保存,也就不存在存儲整數(shù)的字符串表示(String representation)所帶來的額外消耗。
三、使用場景
1.計(jì)數(shù)器
使用思路是:每次有相關(guān)操作的時候,就向Redis服務(wù)器發(fā)送一個incr命令。
例如這樣一個場景:我們有一個web應(yīng)用,我們想記錄每個用戶每天訪問這個網(wǎng)站的次數(shù)。
web應(yīng)用只需要通過拼接用戶id和代表當(dāng)前時間的字符串作為key,每次用戶訪問這個頁面的時候?qū)@個key執(zhí)行一下incr命令。
這個場景可以有很多種擴(kuò)展方法:
通過結(jié)合使用INCR和EXPIRE命令,可以實(shí)現(xiàn)一個只記錄用戶在指定間隔時間內(nèi)的訪問次數(shù)的計(jì)數(shù)器
客戶端可以通過GETSET命令獲取當(dāng)前計(jì)數(shù)器的值并且重置為0
通過類似于DECR或者INCRBY等原子遞增/遞減的命令,可以根據(jù)用戶的操作來增加或者減少某些值 比如在線游戲,需要對用戶的游戲分?jǐn)?shù)進(jìn)行實(shí)時控制,分?jǐn)?shù)可能增加也可能減少。
2.限速器
限速器是一種可以限制某些操作執(zhí)行速率的特殊場景。
傳統(tǒng)的例子就是限制某個公共api的請求數(shù)目。
假設(shè)我們要解決如下問題:限制某個api每秒每個ip的請求次數(shù)不超過10次。
我們可以通過incr命令來實(shí)現(xiàn)兩種方法解決這個問題。
四、流量控制之java實(shí)現(xiàn)
這里我們將在java中使用redis-incr的特性來構(gòu)建一個1分鐘內(nèi)只允許 請求100次的控制代碼,key代表在redis內(nèi)存放的被控制的鍵值。
public static boolean flowControl(String key){
//最大允許100
int max = 100;
long total = 1L;
try {
if (jedisInstance.get(key) == null) {
//jedisInstance是Jedis連接實(shí)例,可以使單鏈接也可以使用鏈接池獲取,實(shí)現(xiàn)方式請參考之前的blog內(nèi)容
//如果redis目前沒有這個key,創(chuàng)建并賦予0,有效時間為60s
jedisInstance.setex(key, 60, "0");
} else {
//獲取加1后的值
total = jedisInstance.incr(redisKey).longValue();
//Redis TTL命令以秒為單位返回key的剩余過期時間。當(dāng)key不存在時,返回-2。當(dāng)key存在但沒有設(shè)置剩余生存時間時,返回-1。否則,以秒為單位,返回key的剩余生存時間。
if (jedisInstance.ttl(redisKey).longValue() == -1L)
{
//為給定key設(shè)置生存時間,當(dāng)key過期時(生存時間為0),它會被自動刪除。
jedisInstance.expire(redisKey, 60);
}
}
} catch (Exception e) {
logger.error("流量控制組件:執(zhí)行計(jì)數(shù)操作失敗,無法執(zhí)行計(jì)數(shù)");
}
long keytotaltransations = max;
//判斷是否已超過最大值,超過則返回false
if (total > keytotaltransations) {
return false;
}
return true;
}
補(bǔ)充知識:【限流算法】java實(shí)現(xiàn)redis分布式時間窗口計(jì)數(shù)器算法
本文實(shí)現(xiàn)一種基于java的分布式固定時間窗口計(jì)數(shù)器算法
//每次請求進(jìn)來,查詢一下當(dāng)前的計(jì)數(shù)值,如果超出請求數(shù)閾值,則拒絕請求,返回系統(tǒng)繁忙提示
private long limitFlow(String key) {
//Setnx(SET if Not eXists) 命令在指定的 key 不存在時,為 key 設(shè)置指定的值。設(shè)置成功返回1,設(shè)置失敗返回0
Long lng = redisCacheClient.setnx(jedisGroup, key, "1");
if (lng == 1) {
//設(shè)置時間窗口,redis-key時效為10秒
redisCacheClient.expire(jedisGroup, key, 10);
return 1L;
} else {
//Redis Incrby 命令將 key 中儲存的數(shù)字加上指定的增量值。相當(dāng)于放在redis中的計(jì)數(shù)器,每次請求到來計(jì)數(shù)器自增1
long val = redisCacheClient.incrBy(jedisGroup, key, 1);
return val;
}
}
以上這篇Redis原子計(jì)數(shù)器incr,防止并發(fā)請求操作就是小編分享給大家的全部內(nèi)容了,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java數(shù)組與二維數(shù)組及替換空格實(shí)戰(zhàn)真題講解
數(shù)組對于每一門編程語言來說都是重要的數(shù)據(jù)結(jié)構(gòu)之一,當(dāng)然不同語言對數(shù)組的實(shí)現(xiàn)及處理也不盡相同。Java?語言中提供的數(shù)組是用來存儲固定大小的同類型元素,這篇文章主要介紹了Java數(shù)組與二維數(shù)組及替換空格實(shí)戰(zhàn)真題講解2022-07-07
SpringAOP+RabbitMQ+WebSocket實(shí)戰(zhàn)詳解
這篇文章主要介紹了SpringAOP+RabbitMQ+WebSocket實(shí)戰(zhàn)詳解,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-11-11
Java實(shí)現(xiàn)簡單點(diǎn)餐系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)簡單點(diǎn)餐系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-01-01
Java程序中使用JavaMail發(fā)送帶圖片和附件的郵件
這篇文章主要介紹了Java程序中使用JavaMail發(fā)送帶圖片和附件的郵件,JavaMail是專門用來處理郵件的Java API,需要的朋友可以參考下2015-11-11

