我們在開發應用的時候,如果需要對某乙個共享變數進行多執行緒同步訪問的時候,可以使用我們學到的鎖進行處理,並且可以完美的執行,毫無bug!
注意這是單機應用,後來業務發展,需要做集群,乙個應用需要部署到幾台機器上然後做負載均衡,大致如下圖:
上圖可以看到,變數a存在三個伺服器記憶體中(這個變數a主要體現是在乙個類中的乙個成員變數,是乙個有狀態的物件),如果不加任何控制的話,變數a同時都會在分配一塊記憶體,三個請求發過來同時對這個變數操作,顯然結果是不對的!即使不是同時發過來,三個請求分別操作三個不同記憶體區域的資料,變數a之間不存在共享,也不具有可見性,處理的結果也是不對的!
如果我們業務中確實存在這個場景的話,我們就需要一種方法解決這個問題!
為了保證乙個方法或屬性在高併發情況下的同一時間只能被同乙個執行緒執行,在傳統單體應用單機部署的情況下,可以使用併發處理相關的功能進行互斥控制。但是,隨著業務發展的需要,原單體單機部署的系統被演化成分布式集群系統後,由於分布式系統多執行緒、多程序並且分布在不同機器上,這將使原單機部署情況下的併發控制鎖策略失效,單純的應用並不能提供分布式鎖的能力。為了解決這個問題就需要一種跨機器的互斥機制來控制共享資源的訪問,這就是分布式鎖要解決的問題!
分布式鎖應該具備哪些條件:
1、在分布式系統環境下,乙個方法在同一時間只能被乙個機器的乙個執行緒執行;
2、高可用的獲取鎖與釋放鎖;
3、高效能的獲取鎖與釋放鎖;
4、具備可重入特性;
5、具備鎖失效機制,防止死鎖;
6、具備非阻塞鎖特性,即沒有獲取到鎖將直接返回獲取鎖失敗
1、選用redis實現分布式鎖原因:
(1)redis有很高的效能;
(2)redis命令對此支援較好,實現起來比較方便
2、使用命令介紹:
(1)setnx
setnx key val:當且僅當key不存在時,set乙個key為val的字串,返回1;若key存在,則什麼都不做,返回0。
(2)expire
expire key timeout:為key設定乙個超時時間,單位為second,超過這個時間鎖會自動釋放,避免死鎖。
(3)delete
delete key:刪除key
在使用redis實現分布式鎖的時候,主要就會使用到這三個命令。
3、實現思想:
(1)獲取鎖的時候,使用setnx加鎖,並使用expire命令為鎖新增乙個超時時間,超過該時間則自動釋放鎖,鎖的value值為乙個隨機生成的uuid,通過此在釋放鎖的時候進行判斷。
(2)獲取鎖的時候還設定乙個獲取的超時時間,若超過這個時間則放棄獲取鎖。
(3)釋放鎖的時候,通過uuid判斷是不是該鎖,若是該鎖,則執行delete進行鎖釋放。
4、 分布式鎖的簡單實現**:
#5、測試剛才實現的分布式鎖例子中使用50個執行緒模擬秒殺10張票,使用–運算子來實現商品減少,從結果有序性就可以看出是否為加鎖狀態。連線redis
import
time
import
uuid
from threading import
thread
import
redis
redis_client = redis.redis(host="
localhost",
port=6379,
#password=123,
db=10)
#獲取乙個鎖
#lock_name:鎖定名稱
#acquire_time: 客戶端等待獲取鎖的時間
#time_out: 鎖的超時時間
def acquire_lock(lock_name, acquire_time=10, time_out=10):
"""獲取乙個分布式鎖
"""identifier =str(uuid.uuid4())
end = time.time() +acquire_time
lock = "
string:lock:
" +lock_name
while time.time()
ifredis_client.setnx(lock, identifier):
#給鎖設定超時時間, 防止程序崩潰導致其他程序無法獲取鎖
redis_client.expire(lock, time_out)
return
identifier
elif
notredis_client.ttl(lock):
redis_client.expire(lock, time_out)
time.sleep(0.001)
return
false
#釋放乙個鎖
defrelease_lock(lock_name, identifier):
"""通用的鎖釋放函式
"""lock = "
string:lock:
" +lock_name
pip =redis_client.pipeline(true)
while
true:
try:
pip.watch(lock)
lock_value =redis_client.get(lock)
ifnot
lock_value:
return
true
if lock_value.decode() ==identifier:
pip.multi()
pip.delete(lock)
pip.execute()
return
true
pip.unwatch()
break
except
redis.excetions.wacthcerror:
pass
return false
count=10defseckill(i):
identifier=acquire_lock('
resource')
print("
執行緒:{}--獲得了鎖
".format(i))
time.sleep(1)
global
count
if count<1:
print("
執行緒:{}--沒搶到,票搶完了
".format(i))
return
count-=1
print("
執行緒:{}--搶到一張票,還剩{}張票
".format(i,count))
release_lock(
'resource
',identifier)
for i in range(50):
t = thread(target=seckill,args=(i,))
t.start()
Python 基於Redis實現乙個簡單的分布式鎖
redis lock.py import redis import time import threading 連線池方式 pool redis.connectionpool host 127.0.0.1 port 6379 redis con redis.redis connection pool...
基於Redis實現的單點登入
背景 每個web應用都有自己的session,那如何在分布式或者集群環境下統一session,即如何實現單點登入,如下圖 解決方案 把session資料存放在redis,統一管理,向外提供服務介面,redis可以設定過期時間,對應session的失效時間 優點 訪問速度快,效率高 無單點故障,可以部...
spring基於redis實現訊息訂閱
本文主要記錄一下如何使用spring基於redis提供的pub sub模型來實現訊息的發布和訂閱處理,只記錄幾個需要注意的切入點.org.springframework.bootgroupid spring boot starter data redisartifactid 2.1.6.releas...