python redis分布式鎖改進
阿新 • • 發佈:2019-05-15
release 代碼 dynamic blog python www name 運行 float
0X01 python redis分布式鎖通用方法
REDIS分布式鎖實現的方式:SETNX + GETSET
使用Redis SETNX 命令實現分布式鎖
python 版本實現上述思路(案例1)
Redis分布式鎖的python實現
但是,流通的代碼 redis鎖中有BUG,有考慮不周的點。
0X02 時間戳變為str形式
time.time() > cls.rdcon.get(cls.lock_key)
寫法不正確。time.time()為浮點型,redis get取得的為字符串型。
python 中,字符串與浮點型對比,字符串大。
>>> a = "0.003" >>> b = 1000000 >>> a >b True >>> a < b False >>>
於是
if cls._lock == 1 or (time.time() > cls.rdcon.get(cls.lock_key) and time.time() > cls.rdcon.getset(cls.lock_key, timestamp)):
變成
if cls._lock == 1 or (time.time() > float(cls.rdcon.get(cls.lock_key)) and time.time() > float(cls.rdcon.getset(cls.lock_key, timestamp))):
0X03 競爭條件下的float
在代碼運行到任何一處地方,鎖都可能釋放。
比如,剛開始拿不到鎖, cls._lock!=1,走向or。
這時鎖釋放了,redis中取出了None,float(None)報錯。
或者getset獲得了None(說明寫入成功),float(None)報錯。
float中可能是數值型,也可能是None型。
改進(同時取函數的第二個參數作為標識id)
#!/usr/bin/env python # coding=utf-8 import time import redis from conf.config import REDIS_HOST, REDIS_PORT, REDIS_PASSWORD class RedisLock(object): def __init__(self): self.rdcon = redis.Redis(host=REDIS_HOST, port=REDIS_PORT, password=REDIS_PASSWORD, db=1) self._lock = 0 self.lock_key = "" @staticmethod def my_float(timestamp): """ Args: timestamp: Returns: float或者0 如果取出的是None,說明原本鎖並沒人用,getset已經寫入,返回0,可以繼續操作。 """ if timestamp: return float(timestamp) else: return 0 @staticmethod def get_lock(cls, key, timeout=10): cls.lock_key = "%s_dynamic_lock" % key while cls._lock != 1: timestamp = time.time() + timeout + 1 cls._lock = cls.rdcon.setnx(cls.lock_key, timestamp) # if 條件中,可能在運行到or之後被釋放,也可能在and之後被釋放 # 將導致 get到一個None,float失敗。 if cls._lock == 1 or ( time.time() > cls.my_float(cls.rdcon.get(cls.lock_key)) and time.time() > cls.my_float(cls.rdcon.getset(cls.lock_key, timestamp))): break else: time.sleep(0.3) @staticmethod def release(cls): if cls.rdcon.get(cls.lock_key) and time.time() < cls.rdcon.get(cls.lock_key): cls.rdcon.delete(cls.lock_key) def redis_lock_deco(cls): def _deco(func): def __deco(*args, **kwargs): cls.get_lock(cls, args[1]) try: return func(*args, **kwargs) finally: cls.release(cls) return __deco return _deco @redis_lock_deco(RedisLock()) def my_func(): print "myfunc() called." time.sleep(20) if __name__ == "__main__": my_func()
python redis分布式鎖改進