2025-04-28 08:31:46 +00:00
|
|
|
|
# coding=utf-8
|
|
|
|
|
|
"""
|
|
|
|
|
|
@project: qabot
|
|
|
|
|
|
@Author:虎
|
|
|
|
|
|
@file: lock.py
|
|
|
|
|
|
@date:2023/9/11 11:45
|
|
|
|
|
|
@desc:
|
|
|
|
|
|
"""
|
2025-07-17 05:24:59 +00:00
|
|
|
|
from functools import wraps
|
2025-04-28 08:31:46 +00:00
|
|
|
|
|
2025-07-17 05:24:59 +00:00
|
|
|
|
import uuid_utils.compat as uuid
|
2025-04-28 08:31:46 +00:00
|
|
|
|
from django.core.cache import caches
|
2025-07-17 05:24:59 +00:00
|
|
|
|
from django_redis import get_redis_connection
|
2025-04-28 08:31:46 +00:00
|
|
|
|
|
|
|
|
|
|
memory_cache = caches['default']
|
|
|
|
|
|
|
2025-07-17 05:24:59 +00:00
|
|
|
|
class RedisLock():
|
|
|
|
|
|
def __init__(self):
|
|
|
|
|
|
self.lock_value = None
|
2025-04-28 08:31:46 +00:00
|
|
|
|
|
2025-07-17 05:24:59 +00:00
|
|
|
|
def try_lock(self, key: str, timeout=None):
|
|
|
|
|
|
"""
|
|
|
|
|
|
获取锁
|
|
|
|
|
|
:param key: 获取锁 key
|
|
|
|
|
|
:param timeout 超时时间
|
|
|
|
|
|
:return: 是否获取到锁
|
|
|
|
|
|
"""
|
|
|
|
|
|
redis_client = get_redis_connection("default")
|
|
|
|
|
|
if timeout is None:
|
|
|
|
|
|
timeout = 3600 # 默认超时时间为3600秒
|
|
|
|
|
|
self.lock_value = str(uuid.uuid7())
|
|
|
|
|
|
return redis_client.set(key, self.lock_value, nx=True, ex=timeout)
|
2025-04-28 08:31:46 +00:00
|
|
|
|
|
|
|
|
|
|
|
2025-07-17 05:24:59 +00:00
|
|
|
|
def un_lock(self, key: str):
|
|
|
|
|
|
"""
|
|
|
|
|
|
解锁
|
|
|
|
|
|
:param key: 解锁 key
|
|
|
|
|
|
:return: 是否解锁成功
|
|
|
|
|
|
"""
|
|
|
|
|
|
redis_client = get_redis_connection("default")
|
|
|
|
|
|
unlock_script = """
|
|
|
|
|
|
if redis.call("get", KEYS[1]) == ARGV[1] then
|
|
|
|
|
|
return redis.call("del", KEYS[1])
|
|
|
|
|
|
else
|
|
|
|
|
|
return 0
|
|
|
|
|
|
end
|
|
|
|
|
|
"""
|
|
|
|
|
|
redis_client.eval(unlock_script, 1, key, self.lock_value)
|
2025-04-28 08:31:46 +00:00
|
|
|
|
|
|
|
|
|
|
|
2025-07-15 10:02:25 +00:00
|
|
|
|
def lock(lock_key, timeout=None):
|
2025-04-28 08:31:46 +00:00
|
|
|
|
"""
|
|
|
|
|
|
给一个函数上锁
|
2025-07-15 10:02:25 +00:00
|
|
|
|
@param lock_key: 上锁key 字符串|函数 函数返回值为字符串
|
|
|
|
|
|
@param timeout: 超时时间
|
2025-04-28 08:31:46 +00:00
|
|
|
|
:return: 装饰器函数 当前装饰器主要限制一个key只能一个线程去调用 相同key只能阻塞等待上一个任务执行完毕 不同key不需要等待
|
2025-07-15 10:02:25 +00:00
|
|
|
|
|
2025-04-28 08:31:46 +00:00
|
|
|
|
"""
|
|
|
|
|
|
|
2025-07-17 05:24:59 +00:00
|
|
|
|
def decorator(func):
|
|
|
|
|
|
@wraps(func)
|
|
|
|
|
|
def wrapper(*args, **kwargs):
|
2025-04-28 08:31:46 +00:00
|
|
|
|
key = lock_key(*args, **kwargs) if callable(lock_key) else lock_key
|
2025-07-17 05:24:59 +00:00
|
|
|
|
rlock = RedisLock()
|
|
|
|
|
|
if not rlock.try_lock(key, timeout):
|
|
|
|
|
|
# 获取锁失败,可自定义异常或返回
|
|
|
|
|
|
return None
|
2025-04-28 08:31:46 +00:00
|
|
|
|
try:
|
2025-07-17 05:24:59 +00:00
|
|
|
|
return func(*args, **kwargs)
|
2025-04-28 08:31:46 +00:00
|
|
|
|
finally:
|
2025-07-17 05:24:59 +00:00
|
|
|
|
rlock.un_lock(key)
|
2025-04-28 08:31:46 +00:00
|
|
|
|
|
2025-07-17 05:24:59 +00:00
|
|
|
|
return wrapper
|
2025-04-28 08:31:46 +00:00
|
|
|
|
|
2025-07-17 05:24:59 +00:00
|
|
|
|
return decorator
|