lock.py 2.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  1. # coding=utf-8
  2. """
  3. @project: qabot
  4. @Author:虎
  5. @file: lock.py
  6. @date:2023/9/11 11:45
  7. @desc:
  8. """
  9. from functools import wraps
  10. import uuid_utils.compat as uuid
  11. from django.core.cache import caches
  12. from django_redis import get_redis_connection
  13. memory_cache = caches['default']
  14. class RedisLock():
  15. def __init__(self):
  16. self.lock_value = None
  17. def try_lock(self, key: str, timeout=None):
  18. """
  19. 获取锁
  20. :param key: 获取锁 key
  21. :param timeout 超时时间
  22. :return: 是否获取到锁
  23. """
  24. redis_client = get_redis_connection("default")
  25. if timeout is None:
  26. timeout = 3600 # 默认超时时间为3600秒
  27. self.lock_value = str(uuid.uuid7())
  28. return redis_client.set(key, self.lock_value, nx=True, ex=timeout)
  29. def un_lock(self, key: str):
  30. """
  31. 解锁
  32. :param key: 解锁 key
  33. :return: 是否解锁成功
  34. """
  35. redis_client = get_redis_connection("default")
  36. unlock_script = """
  37. if redis.call("get", KEYS[1]) == ARGV[1] then
  38. return redis.call("del", KEYS[1])
  39. else
  40. return 0
  41. end
  42. """
  43. redis_client.eval(unlock_script, 1, key, self.lock_value)
  44. def lock(lock_key, timeout=None):
  45. """
  46. 给一个函数上锁
  47. @param lock_key: 上锁key 字符串|函数 函数返回值为字符串
  48. @param timeout: 超时时间
  49. :return: 装饰器函数 当前装饰器主要限制一个key只能一个线程去调用 相同key只能阻塞等待上一个任务执行完毕 不同key不需要等待
  50. """
  51. def decorator(func):
  52. @wraps(func)
  53. def wrapper(*args, **kwargs):
  54. key = lock_key(*args, **kwargs) if callable(lock_key) else lock_key
  55. rlock = RedisLock()
  56. if not rlock.try_lock(key, timeout):
  57. # 获取锁失败,可自定义异常或返回
  58. return None
  59. try:
  60. return func(*args, **kwargs)
  61. finally:
  62. rlock.un_lock(key)
  63. return wrapper
  64. return decorator