用Redis中的zset實現乙個限流器

2021-10-14 09:56:05 字數 2697 閱讀 4525

我還記得14年搶紅公尺的時候,下面這個圖是我最煩的乙個圖

搶了兩個星期,才終於買到了我的第一台小公尺手機:紅公尺1s。小公尺**加入了乙個排隊的機制,於是我們可以感知到自己被限流了,但大部分服務,比如最近各大電商的搶茅台活動,並沒有讓我們感知到限流,不管你是手速不夠還是被限流,都會給你返回「很遺憾,已經被搶光了」類似的提示。不過確實也沒必要讓使用者感知到這個機制(你看,程式設計師又想做產品經理的主了),畢竟結果都是一樣的。

對於這種火爆的活動,為了保證服務的穩定性,都需要對特定的介面進行限流,用redis中的zset實現乙個限流器該怎麼做呢?

限流器需要實現的功能:在指定時間內,允許一定量的請求通過

如圖所示,橫座標代表了時間,座標軸上有乙個視窗順著時間的方向,向前移動。視窗最前面的那條線表示的就是「現在」,每進入乙個請求,就會在時間軸對應的當下時間處打上乙個點。比如我們要實現乙個1分鐘最多100000次訪問的限流器。那麼視窗的大小就是1分鐘,視窗一直向前移動,我們要保證被視窗框住的請求永遠不超過100000個。

使用redis的zset可以很方便的實現這個功能。主要用到以下幾個命令:zremrangebyscore,zcard,zadd。每當乙個請求進入,我們就向zset中新增乙個member,score值是當前時間的毫秒數。member叫什麼不重要,只要保證他不重複就行了。當判斷乙個請求能否通過的時候,就檢測score的值處於「當前時間」和「1分鐘之前」之間的member數量,如果超過了限定值,則被限流,否則加入到zset中,給該請求「放行」。為了保證原子性,我們可以選擇使用lua指令碼來編寫邏輯**。

--keys[1]:該次限流對應的key

--ar**[1]:一分鐘之前的時間戳

--ar**[2]:此時此刻的時間戳

--ar**[3]:允許通過的最大數量

--ar**[4]:member名稱(隨機生成)

redis.

call

('zremrangebyscore'

, keys[1]

,0, ar**[1]

)local res = redis.

call

('zcard'

, keys[1]

)if(res ==

nil)

or(res <

tonumber

(ar**[3]

))then

redis.

call

('zadd'

, keys[1]

, ar**[2]

, ar**[4]

)return

0else

return

1end

寫乙個demo,併發校驗,可以看到輸出(為了方便測試,我設定的是一分鐘最多進入10個請求):

[pool-1-thread-72] info blog20210109.limiter - 進入

[pool-1-thread-16] info blog20210109.limiter - 進入

[pool-1-thread-42] info blog20210109.limiter - 進入

[pool-1-thread-22] info blog20210109.limiter - 進入

[pool-1-thread-91] info blog20210109.limiter - 進入

[pool-1-thread-10] info blog20210109.limiter - 進入

[pool-1-thread-33] info blog20210109.limiter - 進入

[pool-1-thread-83] info blog20210109.limiter - 進入

[pool-1-thread-62] info blog20210109.limiter - 進入

[pool-1-thread-35] info blog20210109.limiter - 進入

[main] info blog20210109.limiter - 一分鐘內進入的請求數有:10

最開始我的指令碼是這樣寫的:

redis.

call

('zremrangebyscore'

, keys[1]

,0, ar**[1]

)local res = redis.

call

('zrangebyscore'

, keys[1]

, ar**[1]

, ar**[2]

)if(res == nil) or (table.

getn

(res)

<

tonumber

(ar**[3]

)) then

redis.

call

('zadd'

, keys[1]

, ar**[2]

, ar**[4]

)return

0else

return

1 end

關於redis中Zset元素的使用

zset在set基礎上增加了乙個排序 權值 需手動指定,分數越小,越排前面 zadd zadd name 序號 value 序號2 value2 依據序號的重要程度排序 zrange zrange name start end 查詢zset中的元素,預設從小到大排序,若要從大到小,使用zrevran...

使用redis中的zset實現實時排行榜

redis在業務開發中會被頻繁使用,zset是其中一種特殊用法,zset具排行榜的天然特性,我前幾個月在一次開發中使用到了zset,就是因為涉及到要實現乙個排行榜,那是我第一次用到zset,雖然之前都看過redis幾種資料型別的資料結構及其使用方法,但是真正用起來的時候,還是有一些細節的東西要處理的...

redis 六 redis的zset(有序集合)

相比於set,zset中會有乙個score屬性,用於set的排名。zadd 向zset中新增元素 sorce value zrem 刪除element zscore 獲取score zincrby 增加score zrange 獲取資料,start到end zrank 通過下標獲取排名 127.0....