243 lines
8.2 KiB
Python
243 lines
8.2 KiB
Python
"""
|
||
库区级别的分布式锁管理器
|
||
用于解决密集库区获取库位时的并发竞争问题
|
||
"""
|
||
|
||
import asyncio
|
||
import time
|
||
from typing import Dict, Optional
|
||
# from utils.logger import Logger
|
||
from config.settings import settings
|
||
from utils.logger import get_logger
|
||
|
||
logger = get_logger("utils.area_lock_manager")
|
||
|
||
|
||
class AreaLockManager:
|
||
"""库区级别的分布式锁管理器"""
|
||
|
||
def __init__(self, default_timeout: Optional[int] = None):
|
||
"""
|
||
初始化锁管理器
|
||
|
||
Args:
|
||
default_timeout: 默认锁超时时间(秒),None时使用配置文件中的值
|
||
"""
|
||
self.locks: Dict[str, Dict[str, any]] = {} # 锁信息存储
|
||
self.lock = asyncio.Lock() # 保护locks字典的锁
|
||
self.default_timeout = default_timeout or settings.AREA_LOCK_DEFAULT_TIMEOUT
|
||
self.cleanup_interval = settings.AREA_LOCK_CLEANUP_INTERVAL
|
||
self.enabled = settings.AREA_LOCK_ENABLE
|
||
|
||
async def acquire_lock(self, lock_key: str, owner_id: str, timeout: Optional[int] = None) -> bool:
|
||
"""
|
||
获取锁
|
||
|
||
Args:
|
||
lock_key: 锁的唯一标识
|
||
owner_id: 锁的拥有者ID(通常是task_record_id)
|
||
timeout: 锁超时时间(秒),None使用默认值
|
||
|
||
Returns:
|
||
bool: 是否成功获取锁
|
||
"""
|
||
# 如果库区锁功能被禁用,直接返回成功
|
||
if not self.enabled:
|
||
logger.debug(f"库区锁功能已禁用,跳过锁获取: {lock_key}")
|
||
return True
|
||
|
||
timeout = timeout or self.default_timeout
|
||
current_time = time.time()
|
||
|
||
async with self.lock:
|
||
# 检查锁是否存在
|
||
if lock_key in self.locks:
|
||
lock_info = self.locks[lock_key]
|
||
|
||
# 检查锁是否过期
|
||
if current_time > lock_info['expires_at']:
|
||
# 锁已过期,清理并重新获取
|
||
logger.info(f"库区锁已过期,清理锁: {lock_key}, 原拥有者: {lock_info['owner_id']}")
|
||
del self.locks[lock_key]
|
||
elif lock_info['owner_id'] == owner_id:
|
||
# 同一个拥有者重新获取锁,更新过期时间
|
||
lock_info['expires_at'] = current_time + timeout
|
||
logger.debug(f"库区锁续期成功: {lock_key}, 拥有者: {owner_id}")
|
||
return True
|
||
else:
|
||
# 锁被其他拥有者占用
|
||
logger.debug(f"库区锁被占用: {lock_key}, 当前拥有者: {lock_info['owner_id']}, 请求者: {owner_id}")
|
||
return False
|
||
|
||
# 获取新锁
|
||
self.locks[lock_key] = {
|
||
'owner_id': owner_id,
|
||
'acquired_at': current_time,
|
||
'expires_at': current_time + timeout
|
||
}
|
||
|
||
logger.debug(f"库区锁获取成功: {lock_key}, 拥有者: {owner_id}, 超时时间: {timeout}秒")
|
||
return True
|
||
|
||
async def release_lock(self, lock_key: str, owner_id: str) -> bool:
|
||
"""
|
||
释放锁
|
||
|
||
Args:
|
||
lock_key: 锁的唯一标识
|
||
owner_id: 锁的拥有者ID
|
||
|
||
Returns:
|
||
bool: 是否成功释放锁
|
||
"""
|
||
# 如果库区锁功能被禁用,直接返回成功
|
||
if not self.enabled:
|
||
logger.debug(f"库区锁功能已禁用,跳过锁释放: {lock_key}")
|
||
return True
|
||
|
||
async with self.lock:
|
||
if lock_key not in self.locks:
|
||
logger.warning(f"尝试释放不存在的库区锁: {lock_key}, 请求者: {owner_id}")
|
||
return False
|
||
|
||
lock_info = self.locks[lock_key]
|
||
if lock_info['owner_id'] != owner_id:
|
||
logger.warning(f"尝试释放他人的库区锁: {lock_key}, 当前拥有者: {lock_info['owner_id']}, 请求者: {owner_id}")
|
||
return False
|
||
|
||
del self.locks[lock_key]
|
||
logger.debug(f"库区锁释放成功: {lock_key}, 拥有者: {owner_id}")
|
||
return True
|
||
|
||
async def is_locked(self, lock_key: str) -> bool:
|
||
"""
|
||
检查锁是否被占用
|
||
|
||
Args:
|
||
lock_key: 锁的唯一标识
|
||
|
||
Returns:
|
||
bool: 锁是否被占用
|
||
"""
|
||
current_time = time.time()
|
||
|
||
async with self.lock:
|
||
if lock_key not in self.locks:
|
||
return False
|
||
|
||
lock_info = self.locks[lock_key]
|
||
if current_time > lock_info['expires_at']:
|
||
# 锁已过期,清理
|
||
del self.locks[lock_key]
|
||
return False
|
||
|
||
return True
|
||
|
||
async def get_lock_info(self, lock_key: str) -> Optional[Dict[str, any]]:
|
||
"""
|
||
获取锁信息
|
||
|
||
Args:
|
||
lock_key: 锁的唯一标识
|
||
|
||
Returns:
|
||
Optional[Dict]: 锁信息,如果锁不存在返回None
|
||
"""
|
||
current_time = time.time()
|
||
|
||
async with self.lock:
|
||
if lock_key not in self.locks:
|
||
return None
|
||
|
||
lock_info = self.locks[lock_key]
|
||
if current_time > lock_info['expires_at']:
|
||
# 锁已过期,清理
|
||
del self.locks[lock_key]
|
||
return None
|
||
|
||
return lock_info.copy()
|
||
|
||
async def cleanup_expired_locks(self):
|
||
"""清理过期的锁"""
|
||
current_time = time.time()
|
||
expired_keys = []
|
||
|
||
async with self.lock:
|
||
for lock_key, lock_info in self.locks.items():
|
||
if current_time > lock_info['expires_at']:
|
||
expired_keys.append(lock_key)
|
||
|
||
for key in expired_keys:
|
||
del self.locks[key]
|
||
logger.info(f"清理过期的库区锁: {key}")
|
||
|
||
return len(expired_keys)
|
||
|
||
async def force_release_all_locks_by_owner(self, owner_id: str) -> int:
|
||
"""
|
||
强制释放指定拥有者的所有锁(用于任务取消或异常情况)
|
||
|
||
Args:
|
||
owner_id: 锁的拥有者ID
|
||
|
||
Returns:
|
||
int: 释放的锁数量
|
||
"""
|
||
released_count = 0
|
||
keys_to_remove = []
|
||
|
||
async with self.lock:
|
||
for lock_key, lock_info in self.locks.items():
|
||
if lock_info['owner_id'] == owner_id:
|
||
keys_to_remove.append(lock_key)
|
||
|
||
for key in keys_to_remove:
|
||
del self.locks[key]
|
||
released_count += 1
|
||
logger.info(f"强制释放库区锁: {key}, 拥有者: {owner_id}")
|
||
|
||
return released_count
|
||
|
||
def get_stats(self) -> Dict[str, any]:
|
||
"""
|
||
获取锁管理器统计信息
|
||
|
||
Returns:
|
||
Dict: 统计信息
|
||
"""
|
||
current_time = time.time()
|
||
active_locks = 0
|
||
expired_locks = 0
|
||
|
||
for lock_info in self.locks.values():
|
||
if current_time > lock_info['expires_at']:
|
||
expired_locks += 1
|
||
else:
|
||
active_locks += 1
|
||
|
||
return {
|
||
'total_locks': len(self.locks),
|
||
'active_locks': active_locks,
|
||
'expired_locks': expired_locks,
|
||
'default_timeout': self.default_timeout
|
||
}
|
||
|
||
|
||
# 全局锁管理器实例
|
||
area_lock_manager = AreaLockManager()
|
||
|
||
|
||
async def start_lock_cleanup_task():
|
||
"""启动锁清理任务"""
|
||
async def cleanup_task():
|
||
while True:
|
||
try:
|
||
await asyncio.sleep(area_lock_manager.cleanup_interval)
|
||
cleaned = await area_lock_manager.cleanup_expired_locks()
|
||
if cleaned > 0:
|
||
logger.info(f"定时清理过期库区锁: {cleaned}个")
|
||
except Exception as e:
|
||
logger.error(f"库区锁清理任务异常: {str(e)}")
|
||
|
||
asyncio.create_task(cleanup_task())
|
||
logger.info(f"库区锁清理任务已启动,清理间隔: {area_lock_manager.cleanup_interval}秒") |