前言
上周“被”上線了一個緊急項目,周五下班接到需求,周一開始思考解決方案,周三開發(fā)完成,周四走流程上線,也算是面向領(lǐng)導編程了。之前的項目里面由于是自運維,然后大多數(shù)又都趕時間,所以在處理定時任務(wù)上面基本都是自己在服務(wù)器上添加crontab,而不是讓多個實例自己去處理定時任務(wù)的并發(fā)鎖,并且Laravel 5.5開始自帶并發(fā)鎖,我們也快升級了。但是這次項目是Python項目,無奈只能自己實現(xiàn)一下,以下這個方案實現(xiàn)起來非常簡單且易于理解。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
import redis r = redis.Redis(...) last_heart = 0 # 記錄上一次得到的鎖心跳 free_lock_try = 6 # 鎖無心跳的最大次數(shù) while not r.setnx( 'mylock' , 1 ): now_heart = r.get( 'mylock' ) print(f "沒獲取到鎖,now_heart={now_heart},last_heart={last_heart},free_lock_try={free_lock_try}" ) if now_heart == last_heart: free_lock_try = free_lock_try - 1 if free_lock_try == 0 : # 鎖已經(jīng) 1 分鐘沒有心跳了 old_heart = r.getset( 'mylock' , 1 ) # 將lock重置為 1 ,并返回set之前的心跳值 if old_heart < now_heart: time.sleep( 10 ) continue else : break # 成功獲取到鎖,退出循環(huán) else : free_lock_try = 6 # 鎖有心跳,重置free_lock_try值 last_heart = now_heart time.sleep( 10 ) def producer_exit(): "" "程序正常退出時候自動清理鎖" "" r.delete( 'mylock' ) import atexit atexit.register(producer_exit) # 業(yè)務(wù)代碼 while True: r.incr( 'mylock' ) # 讓鎖心跳加一 ... |
我們來看看這段程序都解決了并發(fā)鎖中的哪些問題
- 高并發(fā)下,多個進程無法同時獲取到鎖。這里使用的是redis.setnx,如果鎖已經(jīng)存在,其他進程是無法重置鎖并獲取到鎖的。另外當多個進程同時發(fā)現(xiàn)有鎖已經(jīng)沒有心跳了,使用的是redis.getset將心跳重置為1,都能set成功,但是get出來的值多個進程是不一樣的,只有真正獲取到鎖的進程返回的是之前進程的心跳,而其他進程獲取到的都是1。
- 有鎖進程正常退出,可以使用atexit注冊進程退出函數(shù)刪除鎖,這里也可以不要,不過下次啟動得等新的進程等待幾次心跳
- 有鎖進程意外退出,退出后心跳不再增加,超過free_lock_try次數(shù)后,其他進程會重新設(shè)置并獲取鎖
- 所有進程全都意外退出,這個問題不是鎖來關(guān)心的,可以使用supervisor進行守護進程。
導致Redis并發(fā)原因解釋
正所謂只有知其然才能知其所以然,只有弄明白問題出現(xiàn)的原因所在,才能對癥下藥,尋找解決問題的良方。眾所周知,Redis程序采用單線程模式進行運行,作為單線程程序,Redis客戶端的命令是逐條執(zhí)行,也叫做One by One執(zhí)行。既然是逐條命令執(zhí)行,從表面上來看Redis似乎不存在高并發(fā)的問題,這一觀點論也有道理,原子性的Redis命令本身也確實不存在高并發(fā)問題,這與多線程下的程序勃然不同。但是我們項目工作搭建Redis環(huán)境之后,通常都會是一組命令集合執(zhí)行程序,一個請求中就包含了N個Redis執(zhí)行命令,再加上多個客戶端請求,命令就更多了,導致連接超時、數(shù)據(jù)混亂或錯誤、請求阻塞等多種問題。
即總結(jié)為,產(chǎn)生Redis并發(fā)誘因是程序中的業(yè)務(wù)復雜度導致。
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學習或者工作具有一定的參考學習價值,謝謝大家對服務(wù)器之家的支持。
原文鏈接:https://haofly.net/redis-lock-handle-concurrency/