欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

python-redis-lock實(shí)現(xiàn)鎖自動(dòng)續(xù)期的源碼邏輯

 更新時(shí)間:2024年07月12日 10:57:42   作者:重生之我是蔡經(jīng)理  
這篇文章主要介紹了python-redis-lock實(shí)現(xiàn)鎖自動(dòng)續(xù)期的源碼邏輯,其中用到了多線程threading、弱引用weakref和Lua腳本等相關(guān)知識(shí),需要的朋友可以參考下

python-redis-lock簡(jiǎn)介

python-redis-lock是一個(gè)python的第三方庫(kù),基于Redis,封裝了分布式鎖的邏輯,提供了更高級(jí)的API來(lái)簡(jiǎn)化鎖的獲取、保持和釋放過(guò)程。包括自動(dòng)續(xù)期、鎖超時(shí)、重入鎖等功能。
相比于直接使用redis的setnx,避免了寫(xiě)額外代碼來(lái)實(shí)現(xiàn)鎖的復(fù)雜邏輯。

鎖的續(xù)期

在使用分布式鎖的情況下,如果某個(gè)服務(wù)器A的業(yè)務(wù)還未執(zhí)行完(可能因?yàn)榫W(wǎng)絡(luò)擁塞、數(shù)據(jù)量突然變大等各種原因),但是鎖過(guò)期了,可能引發(fā)額外的問(wèn)題。例如另一臺(tái)服務(wù)器B發(fā)現(xiàn)鎖釋放了,于是加鎖并開(kāi)始執(zhí)行業(yè)務(wù),從而導(dǎo)致出現(xiàn)問(wèn)題。
更有可能導(dǎo)致服務(wù)器A業(yè)務(wù)在執(zhí)行完后去釋放鎖時(shí),意外地釋放了服務(wù)器B加的鎖,導(dǎo)致服務(wù)器C進(jìn)入,從而引發(fā)問(wèn)題(在未使用唯一性ID表示加鎖者的情況下)。
所以對(duì)鎖的過(guò)期時(shí)間來(lái)續(xù)期是很有必要的,它確保了鎖在業(yè)務(wù)執(zhí)行完后才釋放。

python-redis-lock實(shí)現(xiàn)自動(dòng)續(xù)期的源碼分析

首先在配置python-redis-lock實(shí)例時(shí),有兩個(gè)參數(shù):expire int 和 auto_renewal boolean。
expire:鎖過(guò)期時(shí)間,單位為秒。
auto_renewal:是否開(kāi)啟自動(dòng)續(xù)期鎖。
python-redis-lock實(shí)例在初始化時(shí),針對(duì)這兩個(gè)參數(shù)的源碼:

class Lock(object):
	def __init__(self, redis_client, name, expire=None, auto_renewal=False, ...):
		...
		if expire:
			expire = int(expire)
		...
		self._expire = expire
		self._lock_renewal_interval = float(expire) * 2/3 if auto_renewal else None
		...

假設(shè)我們?cè)O(shè)置了一個(gè)過(guò)期時(shí)間30秒,開(kāi)啟自動(dòng)續(xù)期的Lock實(shí)例,則self._expire=30秒,self._lock_renewal_interval=20秒。
當(dāng)在代碼層調(diào)用Lock.acquire方法時(shí),判斷self._lock_renewal_interval是否為空,如果不為空,則開(kāi)啟鎖的自動(dòng)續(xù)期(這里省略了一些內(nèi)容,因?yàn)橹魂P(guān)注續(xù)期的代碼實(shí)現(xiàn)邏輯):

class Lock(object):
	def acquire(self, blocking=True, timeout=None):
		...
		if self._lock_renewal_interval is not None:
			self._start_lock_renewer()

在self._start_lock_renewer方法中,基于python的threading模塊,開(kāi)啟了一個(gè)鎖自動(dòng)更新線程self._lock_renewal_thread。這個(gè)線程執(zhí)行self._lock_renewer方法

def _start_lock_renewer(self):
	...
	self._lock_renewal_stop = threading.Event() # threading.Event()用于線程間的協(xié)調(diào)
	self._lock_renewal_thread = threading.Thread(
        group=None,
        target=self._lock_renewer,
        kwargs={
            'name': self._name,
            'lockref': weakref.ref(self), # 對(duì)Lock實(shí)例的弱引用
            'interval': self._lock_renewal_interval,
            'stop': self._lock_renewal_stop,
        },
    )
    self._lock_renewal_thread.demon = True
    self._lock_renewal_thread.start()

weakref.ref(self)創(chuàng)建當(dāng)前類實(shí)例(self)的弱引用,并將其存儲(chǔ)在變量 lockref 中。使用 weakref.ref 創(chuàng)建的弱引用不會(huì)阻止對(duì)象被垃圾回收。
在self._lock_renewer方法中,首先用了while循環(huán)來(lái)每次等待20秒(interval=20)再執(zhí)行循環(huán)體:如果弱引用對(duì)象(即Lock實(shí)例本身)沒(méi)有被垃圾回來(lái),則執(zhí)行續(xù)期的方法(lock.extend):

def _lock_renewer(name, lockref, interval, stop):
	while not stop.wait(timeout=interval):
		...
		lock: "Lock" = lockref()
		if lock is None:
			break
		lock.extend(expire=lock._expire)

在self.extend方法中,執(zhí)行了一個(gè)Lua腳本來(lái)更新鎖的過(guò)期時(shí)間。
在滿足self._name和self._signal的緩存鍵值存在且鎖未過(guò)期的情況下,將當(dāng)前鎖的過(guò)期時(shí)間重置為expire(30秒)。

def extend(self. expire=None):
	if expire:
		expire = int(expire)
	error = self.extend_script(
		client=self._client, # 對(duì)redis的連接
		keys=(self._name, self._signal),
		args=(self._id, expire))
	...
# self.extend_script的執(zhí)行邏輯:
EXTEND_SCRIPT = b"""
    if redis.call("get", KEYS[1]) ~= ARGV[1] then
        return 1
    elseif redis.call("ttl", KEYS[1]) < 0 then
        return 2
    else
        redis.call("expire", KEYS[1], ARGV[2])
        return 0
    end
"""
cls.extend_script = redis_client.register_script(EXTEND_SCRIPT)

以上就是python-redis-lock實(shí)現(xiàn)鎖自動(dòng)續(xù)期的源碼邏輯。
其中用到了多線程threading、弱引用weakref和Lua腳本等相關(guān)知識(shí)。

到此這篇關(guān)于python-redis-lock是如何實(shí)現(xiàn)鎖自動(dòng)續(xù)期的的文章就介紹到這了,更多相關(guān)python-redis-lock鎖自動(dòng)續(xù)期內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論