redis_lock.py 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. # -*- coding: utf-8 -*-
  2. """
  3. Created on 2019/11/5 5:25 PM
  4. ---------
  5. @summary:
  6. ---------
  7. @author: Boris
  8. @email: boris_liu@foxmail.com
  9. """
  10. import threading
  11. import time
  12. from feapder.db.redisdb import RedisDB
  13. from feapder.utils.log import log
  14. class RedisLock:
  15. redis_cli = None
  16. def __init__(
  17. self, key, *, wait_timeout=0, lock_timeout=86400, redis_cli=None, redis_url=None
  18. ):
  19. """
  20. redis超时锁
  21. :param key: 存储锁的key redis_lock:[key]
  22. :param wait_timeout: 等待加锁超时时间,为0时则不等待加锁,加锁失败
  23. :param lock_timeout: 锁超时时间 为0时则不会超时,直到锁释放或意外退出,默认超时为1天
  24. :param redis_cli: redis客户端对象
  25. :param redis_url: redis连接地址,若redis_cli传值,则不使用redis_url
  26. 用法示例:
  27. with RedisLock(key="test") as _lock:
  28. if _lock.locked:
  29. # 用来判断是否加上了锁
  30. # do somethings
  31. """
  32. self.redis_conn = redis_cli
  33. self.redis_url = redis_url
  34. self.lock_key = "redis_lock:{}".format(key)
  35. # 锁超时时间
  36. self.lock_timeout = lock_timeout
  37. # 等待加锁时间
  38. self.wait_timeout = wait_timeout
  39. self.locked = False
  40. self.stop_prolong_life = False
  41. @property
  42. def redis_conn(self):
  43. if not self.__class__.redis_cli:
  44. self.__class__.redis_cli = RedisDB(url=self.redis_url).get_redis_obj()
  45. return self.__class__.redis_cli
  46. @redis_conn.setter
  47. def redis_conn(self, cli):
  48. if cli:
  49. self.__class__.redis_cli = cli
  50. def __enter__(self):
  51. if not self.locked:
  52. self.acquire()
  53. if self.locked:
  54. # 延长锁的时间
  55. thread = threading.Thread(target=self.prolong_life)
  56. thread.setDaemon(True)
  57. thread.start()
  58. return self
  59. def __exit__(self, exc_type, exc_val, exc_tb):
  60. self.stop_prolong_life = True
  61. self.release()
  62. def __repr__(self):
  63. return "<RedisLock: {} >".format(self.lock_key)
  64. def acquire(self):
  65. start = time.time()
  66. while True:
  67. # 尝试加锁
  68. if self.redis_conn.set(self.lock_key, time.time(), nx=True, ex=5):
  69. self.locked = True
  70. break
  71. if self.wait_timeout > 0:
  72. if time.time() - start > self.wait_timeout:
  73. log.info("加锁失败")
  74. break
  75. else:
  76. break
  77. log.debug("等待加锁: {} wait:{}".format(self, time.time() - start))
  78. if self.wait_timeout > 10:
  79. time.sleep(5)
  80. else:
  81. time.sleep(1)
  82. return
  83. def release(self):
  84. if self.locked:
  85. self.redis_conn.delete(self.lock_key)
  86. self.locked = False
  87. return
  88. def prolong_life(self):
  89. """
  90. 延长锁的过期时间
  91. :return:
  92. """
  93. spend_time = 0
  94. while not self.stop_prolong_life:
  95. expire = self.redis_conn.ttl(self.lock_key)
  96. if expire < 0: # key 不存在
  97. time.sleep(1)
  98. continue
  99. self.redis_conn.expire(self.lock_key, expire + 5) # 延长5秒
  100. time.sleep(expire) # 临过期5秒前,再次延长
  101. spend_time += expire
  102. if self.lock_timeout and spend_time > self.lock_timeout:
  103. log.info("锁超时,释放")
  104. self.redis_conn.delete(self.lock_key)
  105. break