# -*- coding: utf-8 -*- """ Created on 2019/11/5 5:25 PM --------- @summary: --------- @author: Boris @email: boris_liu@foxmail.com """ import threading import time from feapder.db.redisdb import RedisDB from feapder.utils.log import log class RedisLock: redis_cli = None def __init__(self, key, redis_cli=None, wait_timeout=0, lock_timeout=86400): """ redis超时锁 :param key: 存储锁的key redis_lock:[key] :param redis_cli: redis客户端对象 :param wait_timeout: 等待加锁超时时间,为0时则不等待加锁,加锁失败 :param lock_timeout: 锁超时时间 为0时则不会超时,直到锁释放或意外退出,默认超时为1天 用法示例: with RedisLock(key="test") as _lock: if _lock.locked: # 用来判断是否加上了锁 # do somethings """ self.redis_conn = redis_cli self.lock_key = "redis_lock:{}".format(key) # 锁超时时间 self.lock_timeout = lock_timeout # 等待加锁时间 self.wait_timeout = wait_timeout self.locked = False self.stop_prolong_life = False @property def redis_conn(self): if not self.__class__.redis_cli: self.__class__.redis_cli = RedisDB().get_redis_obj() return self.__class__.redis_cli @redis_conn.setter def redis_conn(self, cli): self.__class__.redis_cli = cli def __enter__(self): if not self.locked: self.acquire() # 延长锁的时间 thread = threading.Thread(target=self.prolong_life) thread.setDaemon(True) thread.start() return self def __exit__(self, exc_type, exc_val, exc_tb): self.stop_prolong_life = True self.release() def __repr__(self): return "".format(self.lock_key) def acquire(self): start = time.time() while True: # 尝试加锁 if self.redis_conn.set(self.lock_key, time.time(), nx=True, ex=5): self.locked = True break if self.wait_timeout > 0: if time.time() - start > self.wait_timeout: log.info("加锁失败") break else: break log.debug("等待加锁: {} wait:{}".format(self, time.time() - start)) if self.wait_timeout > 10: time.sleep(5) else: time.sleep(1) return def release(self): if self.locked: self.redis_conn.delete(self.lock_key) self.locked = False return def prolong_life(self): """ 延长锁的过期时间 :return: """ spend_time = 0 while not self.stop_prolong_life: expire = self.redis_conn.ttl(self.lock_key) if expire < 0: # key 不存在 time.sleep(1) continue self.redis_conn.expire(self.lock_key, expire + 5) # 延长5秒 time.sleep(expire) # 临过期5秒前,再次延长 spend_time += expire if self.lock_timeout and spend_time > self.lock_timeout: log.info("锁超时,释放") self.redis_conn.delete(self.lock_key) break