zoukankan      html  css  js  c++  java
  • 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()
    
    
  • 相关阅读:
    Eclipse中properties文件中文显示编码、乱码问题
    Eclipse中安装yml插件( YEdit )
    Java中如何返回Json数组
    ASIFormDataRequest 登录
    Safari里使用JsonView
    beginUpdates和endUpdates
    svn log 不显示日志的问题
    svn代码回滚命令
    Tomcat: localhost:8080 提示404
    android定时三种方式
  • 原文地址:https://www.cnblogs.com/huim/p/10868513.html
Copyright © 2011-2022 走看看