利用Redis實(shí)現(xiàn)訪問次數(shù)限流的方法詳解
假設(shè)我們要做一個(gè)業(yè)務(wù)需求,這個(gè)需求就是限制用戶的訪問頻次。比如1分鐘內(nèi)只能訪問20次,10分鐘內(nèi)只能訪問200次。因?yàn)槭怯脩艟S度的場(chǎng)景,性能肯定是要首先考慮,那么適合這個(gè)場(chǎng)景的非Redis莫屬。
最簡(jiǎn)單的實(shí)現(xiàn),莫過于只是用incr進(jìn)行計(jì)數(shù)操作,于是有了下面的代碼:
long count = redisTemplate.opsForValue().increment("user:1:60");
if (count > maxLimitCount) {
throw new LimitException("訪問太頻繁");
}
count = redisTemplate.opsForValue().increment("user:1:600");
if (count > maxLimitCount) {
throw new LimitException("訪問太頻繁");
}來,我們對(duì)上面這段代碼解讀一下。需求有2個(gè)時(shí)間維度的限制,所以這邊基于用戶和時(shí)間維度構(gòu)建了Redis的Key。然后對(duì)每個(gè)Key進(jìn)行計(jì)數(shù),計(jì)數(shù)后的結(jié)果用于跟限制的值進(jìn)行判斷,如果超出了限制的值就拋出異常。
假設(shè)限制的時(shí)間場(chǎng)景有10個(gè)呢?那上面的代碼是不是得寫10遍才可以。有人可能會(huì)說,這還不簡(jiǎn)單嗎?循環(huán)呀,循環(huán)確實(shí)能夠解決這個(gè)問題。但是大家有沒有去思考,這是用戶維度的請(qǐng)求,如果每個(gè)請(qǐng)求里面都去操作10次Redis的話,這耗時(shí)至少也得10來毫秒吧。所以問題在這,并不是說這個(gè)邏輯實(shí)現(xiàn)的有問題。
那我們就改成批量的吧,用pipeline來批量執(zhí)行。
redisTemplate.execute(new RedisCallback<Long>() {
@Override
public Long doInRedis(RedisConnection connection) throws DataAccessException {
connection.openPipeline();
connection.incr("user:1:60".getBytes());
connection.incr("user:1:600".getBytes());
onnection.closePipeline();
return null;
}
});用pipeline也有一個(gè)問題,那就是拿不到返回值,也就只能增加,但是沒辦法判斷是否超過了限制的閥值。
所以需要在第一步先查詢下,用查到的值進(jìn)行判斷,這樣也就是只需要和Redis交互兩次就可以了。
上面的代碼在單節(jié)點(diǎn)下沒問題,但是如果在集群下,其實(shí)每個(gè)Key都可能分配到不同的節(jié)點(diǎn)上去,只不過是底層幫你屏蔽掉了細(xì)節(jié),并發(fā)執(zhí)行,拿到了所有結(jié)果后合并返回的。所以我們需要讓所有的Key都路由到一個(gè)節(jié)點(diǎn)上,本來就是用戶維度的,直接使用userId路由即可。
這個(gè)時(shí)候Redis的HashTag功能就排上用場(chǎng)了,將Key user:1:600改寫成user:{1}:600 。
雖然已經(jīng)優(yōu)化了,但是還是要發(fā)起兩次網(wǎng)絡(luò)請(qǐng)求才能完成這個(gè)邏輯,有沒有可能再進(jìn)一步優(yōu)化下呢?一次請(qǐng)求行不行。
這個(gè)時(shí)候要放大招了,Lua腳本走起,將所有邏輯都放入Lua腳本中,一次網(wǎng)絡(luò)交互即可完成。
local current
current = redis.call("incr",KEYS[1])
if current == 1 then
redis.call("expire",KEYS[1],1)
end
if current > ARGV[1]
return 1
end
return 0上面腳本演示了如何對(duì)一個(gè)Key進(jìn)行處理,返回1表示限流,返回0表示通過。不過使用lua腳本的時(shí)候要注意,某些云服務(wù)的Redis會(huì)對(duì)腳本進(jìn)行校驗(yàn),像Redis的Key不能使用變量,必須用KEYS[下標(biāo)]的方式,所以這里操作多個(gè)Key還不能用循環(huán),代碼得寫多遍,這是一個(gè)惡心的點(diǎn)。
總結(jié)
到此這篇關(guān)于利用Redis實(shí)現(xiàn)訪問次數(shù)限流的文章就介紹到這了,更多相關(guān)Redis訪問次數(shù)限流內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
深入了解Redis連接數(shù)問題的現(xiàn)象和解法
一般情況?Redis?連接數(shù)問題并不常見,但是當(dāng)你業(yè)務(wù)服務(wù)增加、對(duì)?Redis?的依賴持續(xù)增強(qiáng)的過程中,可能會(huì)遇到很多?Redis?的問題,這個(gè)時(shí)候,Redis?連接數(shù)可能就成了一個(gè)常見的問題,在本章節(jié),希望能夠帶大家了解Redis連接數(shù)問題的現(xiàn)象和解法,需要的朋友可以參考下2023-12-12
redis實(shí)現(xiàn)存儲(chǔ)帖子的點(diǎn)贊狀態(tài)和數(shù)量的示例代碼
使用Redis來實(shí)現(xiàn)點(diǎn)贊功能是一種高效的選擇,因?yàn)镽edis是一個(gè)內(nèi)存數(shù)據(jù)庫,適用于處理高并發(fā)的數(shù)據(jù)操作,這篇文章主要介紹了redis實(shí)現(xiàn)存儲(chǔ)帖子的點(diǎn)贊狀態(tài)和數(shù)量的示例代碼,需要的朋友可以參考下2023-09-09
在CenOS系統(tǒng)下安裝和配置Redis數(shù)據(jù)庫的教程
這篇文章主要介紹了在CenOS系統(tǒng)下安裝和配置Redis數(shù)據(jù)庫的教程,Redis是一個(gè)可基于內(nèi)存的高性能NoSQL數(shù)據(jù)庫,需要的朋友可以參考下2015-11-11
動(dòng)態(tài)添加Redis密碼認(rèn)證的方法
本篇文章主要介紹了動(dòng)態(tài)添加Redis密碼認(rèn)證的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-06-06
Redis核心原理與實(shí)踐之字符串實(shí)現(xiàn)原理
這本書深入地分析了Redis常用特性的內(nèi)部機(jī)制與實(shí)現(xiàn)方式,內(nèi)容源自對(duì)Redis源碼的分析,并從中總結(jié)出設(shè)計(jì)思路、實(shí)現(xiàn)原理。對(duì)Redis字符串實(shí)現(xiàn)原理相關(guān)知識(shí)感興趣的朋友一起看看吧2021-09-09
Redis如何實(shí)現(xiàn)數(shù)據(jù)庫讀寫分離詳解
Redis的主從架構(gòu),能幫助我們實(shí)現(xiàn)讀多,寫少的情況,下面這篇文章主要給大家介紹了關(guān)于Redis如何實(shí)現(xiàn)數(shù)據(jù)庫讀寫分離的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。2018-03-03
Redis定時(shí)任務(wù)原理的實(shí)現(xiàn)
本文主要是基于?redis?6.2?源碼進(jìn)行分析定時(shí)事件的數(shù)據(jù)結(jié)構(gòu)和常見操作,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03

