|
|
|
|
from functools import wraps
|
|
|
|
|
|
|
|
|
|
from redis_lock import Lock as RedisLock
|
|
|
|
|
from redis import Redis
|
|
|
|
|
|
|
|
|
|
from common.utils import get_logger
|
|
|
|
|
from common.utils.inspect import copy_function_args
|
|
|
|
|
from apps.jumpserver.const import CONFIG
|
|
|
|
|
|
|
|
|
|
logger = get_logger(__file__)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class AcquireFailed(RuntimeError):
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class DistributedLock(RedisLock):
|
|
|
|
|
def __init__(self, name, blocking=True, expire=60*2, auto_renewal=True):
|
|
|
|
|
"""
|
|
|
|
|
使用 redis 构造的分布式锁
|
|
|
|
|
|
|
|
|
|
:param name:
|
|
|
|
|
锁的名字,要全局唯一
|
|
|
|
|
:param blocking:
|
|
|
|
|
该参数只在锁作为装饰器或者 `with` 时有效。
|
|
|
|
|
:param expire:
|
|
|
|
|
锁的过期时间,注意不一定是锁到这个时间就释放了,分两种情况
|
|
|
|
|
当 `auto_renewal=False` 时,锁会释放
|
|
|
|
|
当 `auto_renewal=True` 时,如果过期之前程序还没释放锁,我们会延长锁的存活时间。
|
|
|
|
|
这里的作用是防止程序意外终止没有释放锁,导致死锁。
|
|
|
|
|
"""
|
|
|
|
|
self.kwargs_copy = copy_function_args(self.__init__, locals())
|
|
|
|
|
redis = Redis(host=CONFIG.REDIS_HOST, port=CONFIG.REDIS_PORT, password=CONFIG.REDIS_PASSWORD)
|
|
|
|
|
super().__init__(redis_client=redis, name=name, expire=expire, auto_renewal=auto_renewal)
|
|
|
|
|
self._blocking = blocking
|
|
|
|
|
|
|
|
|
|
def __enter__(self):
|
|
|
|
|
acquired = self.acquire(blocking=self._blocking)
|
|
|
|
|
if self._blocking and not acquired:
|
|
|
|
|
raise EnvironmentError("Lock wasn't acquired, but blocking=True")
|
|
|
|
|
if not acquired:
|
|
|
|
|
raise AcquireFailed
|
|
|
|
|
return self
|
|
|
|
|
|
|
|
|
|
def __exit__(self, exc_type=None, exc_value=None, traceback=None):
|
|
|
|
|
self.release()
|
|
|
|
|
|
|
|
|
|
def __call__(self, func):
|
|
|
|
|
@wraps(func)
|
|
|
|
|
def inner(*args, **kwds):
|
|
|
|
|
# 要创建一个新的锁对象
|
|
|
|
|
with self.__class__(**self.kwargs_copy):
|
|
|
|
|
return func(*args, **kwds)
|
|
|
|
|
|
|
|
|
|
return inner
|