Files
xingrin/backend/apps/engine/services/worker_load_service.py

148 lines
4.3 KiB
Python
Raw Normal View History

2025-12-12 18:04:57 +08:00
"""
Worker 负载服务Redis
存储结构
- worker:load:{worker_id} - Hash: {cpu, mem, updated}
- TTL: 60 超时自动清理
"""
import logging
from typing import Optional, Dict, Any
from datetime import datetime
import redis
from django.conf import settings
logger = logging.getLogger(__name__)
class WorkerLoadService:
"""Worker 负载数据服务(基于 Redis"""
# Key 前缀
KEY_PREFIX = "worker:load:"
# 数据过期时间(秒)- 超过此时间未更新视为离线
# 心跳间隔 3 秒TTL 设为 15 秒5 次心跳容错)
TTL_SECONDS = 15
def __init__(self):
self._redis: Optional[redis.Redis] = None
@property
def redis(self) -> redis.Redis:
"""懒加载 Redis 连接"""
if self._redis is None:
self._redis = redis.Redis(
host=settings.REDIS_HOST,
port=settings.REDIS_PORT,
db=settings.REDIS_DB,
decode_responses=True,
)
return self._redis
def _key(self, worker_id: int) -> str:
"""生成 Redis key"""
return f"{self.KEY_PREFIX}{worker_id}"
def update_load(self, worker_id: int, cpu_percent: float, memory_percent: float) -> bool:
"""
更新 Worker 负载数据
Args:
worker_id: Worker ID
cpu_percent: CPU 使用率
memory_percent: 内存使用率
Returns:
是否成功
"""
try:
key = self._key(worker_id)
data = {
"cpu": cpu_percent,
"mem": memory_percent,
"updated": datetime.now().isoformat(),
}
# 使用 pipeline 原子操作
pipe = self.redis.pipeline()
pipe.hset(key, mapping=data)
pipe.expire(key, self.TTL_SECONDS)
pipe.execute()
return True
except Exception as e:
logger.error(f"更新 Worker 负载失败 - ID: {worker_id}: {e}")
return False
def get_load(self, worker_id: int) -> Optional[Dict[str, Any]]:
"""
获取 Worker 负载数据
Returns:
{"cpu": float, "mem": float, "updated": str} None
"""
try:
key = self._key(worker_id)
data = self.redis.hgetall(key)
if not data:
return None
return {
"cpu": float(data.get("cpu", 0)),
"mem": float(data.get("mem", 0)),
"updated": data.get("updated", ""),
}
except Exception as e:
logger.error(f"获取 Worker 负载失败 - ID: {worker_id}: {e}")
return None
def get_all_loads(self, worker_ids: list[int]) -> Dict[int, Dict[str, Any]]:
"""
批量获取 Worker 负载数据
Args:
worker_ids: Worker ID 列表
Returns:
{worker_id: {"cpu": float, "mem": float}} 字典
"""
result = {}
try:
pipe = self.redis.pipeline()
for worker_id in worker_ids:
pipe.hgetall(self._key(worker_id))
responses = pipe.execute()
for worker_id, data in zip(worker_ids, responses):
if data:
result[worker_id] = {
"cpu": float(data.get("cpu", 0)),
"mem": float(data.get("mem", 0)),
}
except Exception as e:
logger.error(f"批量获取 Worker 负载失败: {e}")
return result
def delete_load(self, worker_id: int) -> bool:
"""删除 Worker 负载数据"""
try:
self.redis.delete(self._key(worker_id))
return True
except Exception as e:
logger.error(f"删除 Worker 负载失败 - ID: {worker_id}: {e}")
return False
def is_online(self, worker_id: int) -> bool:
"""检查 Worker 是否在线Redis 中有数据且未过期)"""
return self.redis.exists(self._key(worker_id)) > 0
# 单例
worker_load_service = WorkerLoadService()