redis示例 限速器,計時器

2022-03-02 07:22:59 字數 3005 閱讀 1345

incr key

key中儲存的數字值增一。

如果key不存在,那麼key的值會先被初始化為0,然後再執行 incr 操作。

如果值包含錯誤的型別,或字串型別的值不能表示為數字,那麼返回乙個錯誤。

本操作的值限制在 64 位(bit)有符號數字表示之內。

這是乙個針對字串的操作,因為 redis 沒有專用的整數型別,所以 key 內儲存的字串被解釋為十進位制 64 位有符號整數來執行 incr 操作。

可用版本:>= 1.0.0

時間複雜度:o(1)

返回值:執行 incr 命令之後key的值。

redis> set page_view 20

okredis> incr page_view

(integer) 21

redis> get page_view # 數字值在 redis 中以字串的形式儲存

"21"

計數器是 redis 的原子性自增操作可實現的最直觀的模式了,它的想法相當簡單:每當某個操作發生時,向 redis 傳送乙個 incr 命令。

比如在乙個 web 應用程式中,如果想知道使用者在一年中每天的點選量,那麼只要將使用者 id 以及相關的日期資訊作為鍵,並在每次使用者點選頁面時,執行一次自增操作即可。

比如使用者名稱是peter,點選時間是 2012 年 3 月 22 日,那麼執行命令incr peter::2012.3.22

可以用以下幾種方式擴充套件這個簡單的模式:

限速器是特殊化的計算器,它用於限制乙個操作可以被執行的速率(rate)。

function limit_api_call(ip)

ts = current_unix_time()

keyname = ip+":"+ts

current = get(keyname)

if current != null and current > 10 then

error "too many requests per second"

endif current == null then

multi

incr(keyname, 1)

expire(keyname, 1)

exec

else

incr(keyname, 1)

endperform_api_call()

這個實現每秒鐘為每個 ip 位址使用乙個不同的計數器,並用 expire 命令設定生存時間(這樣 redis 就會負責自動刪除過期的計數器)。

注意,我們使用事務打包執行 incr 命令和 expire 命令,避免引入競爭條件,保證每次呼叫 api 時都可以正確地對計數器進行自增操作並設定生存時間。

以下是另乙個限速器實現:

function limit_api_call(ip):

current = get(ip)

if current != null and current > 10 then

error "too many requests per second"

else

value = incr(ip)

if value == 1 then

expire(ip,1)

endperform_api_call()

end

這個限速器只使用單個計數器,它的生存時間為一秒鐘,如果在一秒鐘內,這個計數器的值大於10的話,那麼訪問就會被禁止。

這個新的限速器在思路方面是沒有問題的,但它在實現方面不夠嚴謹,如果我們仔細觀察一下的話,就會發現在 incr 和 expire 之間存在著乙個競爭條件,假如客戶端在執行 incr 之後,因為某些原因(比如客戶端失敗)而忘記設定 expire 的話,那麼這個計數器就會一直存在下去,造成每個使用者只能訪問10次,噢,這簡直是個災難!

要消滅這個實現中的競爭條件,我們可以將它轉化為乙個 lua 指令碼,並放到 redis 中執行(這個方法僅限於 redis 2.6 及以上的版本):

local current

current = redis.call("incr",keys[1])

if tonumber(current) == 1 then

redis.call("expire",keys[1],1)

end

通過將計數器作為指令碼放到 redis 上執行,我們保證了 incr 和 expire 兩個操作的原子性,現在這個指令碼實現不會引入競爭條件,它可以運作的很好。

關於在 redis 中執行 lua 指令碼的更多資訊,請參考 eval 命令。

還有另一種消滅競爭條件的方法,就是使用 redis 的列表結構來代替 incr 命令,這個方法無須指令碼支援,因此它在 redis 2.6 以下的版本也可以執行得很好:

function limit_api_call(ip)

current = llen(ip)

if current > 10 then

error "too many requests per second"

else

if exists(ip) == false

multi

rpush(ip,ip)

expire(ip,1)

exec

else

rpushx(ip,ip)

endperform_api_call()

end

新的限速器使用了列表結構作為容器, llen 用於對訪問次數進行檢查,乙個事務包裹著 rpush 和 expire 兩個命令,用於在第一次執行計數時建立列表,並正確設定地設定過期時間,最後, rpushx 在後續的計數操作中進行增加操作。

python計時器 python 計時器

import sysclassshowtime qwidget def init self super init self.istimestart false 標記時間是否開始計時 self.setwindowtitle qlable 顯示計時時間 self.lable time val qlabe...

python計時器單位 python計時器類

import time as t class mytimer def init self self.unit 年 月 日 時 分 秒 self.prompt 未開始計時 self.lasted self.start 0 self.stop 0 def str self return self.pro...

伺服器計時器與 Windows 計時器

說明 此文大部分內容摘自msdn,本人只是做了一點整理。如果有什麼異議,一切以msdn為準。一 引言 在 visual studio net 中有兩種計時器控制項 基於伺服器的計時器和標準的基於 windows 的計時器。基於 windows 的計時器為在 windows 窗體應用程式中使用而進行了...