REDIS分布式锁实现的方式:SETNX + GETSET
python 版本实现上述思路(案例1)
但是,流通的代码 redis锁中有BUG,有考虑不周的点。
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))):
在代码运行到任何一处地方,锁都可能释放。
比如,刚开始拿不到锁, 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()
原文:https://www.cnblogs.com/huim/p/10868513.html