python redis分布式锁改进

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()

转载于:https://www.cnblogs.com/huim/p/10868513.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值