refactor(WS): 使用连接池上下文管理器简化连接管理

重构 WS 类中的连接获取和释放逻辑,使用 connection 上下文管理器确保连接正确释放。
同时改进消息处理循环中的异常处理和连接管理。

refactor(ws_pool): 增强连接池的健壮性和管理能力

1. 添加连接上下文管理器支持
2. 改进连接获取和释放逻辑,增加连接计数和锁保护
3. 优化连接健康检查和清理机制
4. 增强错误处理和日志记录

fix(bot_status): 增加系统信息获取和渲染的错误处理

1. 为系统信息获取添加超时和错误处理
2. 为Redis数据获取添加异常捕获
3. 为图片渲染添加异常处理
4. 改进日志记录和用户反馈
This commit is contained in:
2026-01-23 17:15:07 +08:00
parent 15dcf0f764
commit c851b49db9
3 changed files with 232 additions and 114 deletions

View File

@@ -6,6 +6,7 @@ Bot 状态查询插件
import os
import psutil
import time
import asyncio
from datetime import datetime, timedelta
from core.bot import Bot
@@ -32,15 +33,23 @@ def _get_system_info():
"""
同步函数:使用 psutil 获取系统信息,避免阻塞事件循环。
"""
# interval=1 会阻塞1秒必须在线程池中运行
cpu_percent = psutil.cpu_percent(interval=1)
mem_info = psutil.virtual_memory()
bot_mem_mb = PROCESS.memory_info().rss / (1024 * 1024)
return {
"cpu_percent": f"{cpu_percent:.1f}",
"mem_percent": f"{mem_info.percent:.1f}",
"bot_mem_mb": f"{bot_mem_mb:.2f}",
}
try:
# interval=1 会阻塞1秒必须在线程池中运行
cpu_percent = psutil.cpu_percent(interval=1)
mem_info = psutil.virtual_memory()
bot_mem_mb = PROCESS.memory_info().rss / (1024 * 1024)
return {
"cpu_percent": f"{cpu_percent:.1f}",
"mem_percent": f"{mem_info.percent:.1f}",
"bot_mem_mb": f"{bot_mem_mb:.2f}",
}
except Exception as e:
logger.error(f"获取系统信息失败: {e}")
return {
"cpu_percent": "N/A",
"mem_percent": "N/A",
"bot_mem_mb": "N/A",
}
@matcher.command("status", "状态")
async def handle_status(bot: Bot, event: MessageEvent, args: list[str]):
@@ -93,26 +102,51 @@ async def handle_status(bot: Bot, event: MessageEvent, args: list[str]):
}
# 3. 获取统计数据
msgs_recv = await redis_manager.get("neobot:stats:messages_received") or 0
msgs_sent = await redis_manager.get("neobot:stats:messages_sent") or 0
command_stats_raw = await redis_manager.redis.hgetall("neobot:command_stats")
total_commands = sum(int(v) for v in command_stats_raw.values())
stats_data = {
"messages_received": int(msgs_recv),
"messages_sent": int(msgs_sent),
"total_commands": total_commands,
}
try:
msgs_recv = await redis_manager.get("neobot:stats:messages_received") or 0
msgs_sent = await redis_manager.get("neobot:stats:messages_sent") or 0
command_stats_raw = await redis_manager.redis.hgetall("neobot:command_stats")
total_commands = sum(int(v) for v in command_stats_raw.values())
stats_data = {
"messages_received": int(msgs_recv),
"messages_sent": int(msgs_sent),
"total_commands": total_commands,
}
command_stats_data = sorted(
[{"name": k, "count": int(v)} for k, v in command_stats_raw.items()],
key=lambda x: x["count"],
reverse=True
)
command_stats_data = sorted(
[{"name": k, "count": int(v)} for k, v in command_stats_raw.items()],
key=lambda x: x["count"],
reverse=True
)
except Exception as e:
logger.error(f"获取Redis统计数据失败: {e}")
stats_data = {
"messages_received": 0,
"messages_sent": 0,
"total_commands": 0,
}
command_stats_data = []
# 4. 异步获取系统信息
system_data = await run_in_thread_pool(_get_system_info)
# 设置超时,防止 psutil 阻塞过久
try:
system_data = await asyncio.wait_for(run_in_thread_pool(_get_system_info), timeout=5.0)
except asyncio.TimeoutError:
logger.error("获取系统信息超时")
system_data = {
"cpu_percent": "Timeout",
"mem_percent": "Timeout",
"bot_mem_mb": "Timeout",
}
except Exception as e:
logger.error(f"获取系统信息异常: {e}")
system_data = {
"cpu_percent": "Error",
"mem_percent": "Error",
"bot_mem_mb": "Error",
}
# 5. 准备模板所需的所有数据
template_data = {
@@ -125,18 +159,22 @@ async def handle_status(bot: Bot, event: MessageEvent, args: list[str]):
}
# 6. 渲染图片
base64_str = await image_manager.render_template_to_base64(
template_name="status.html",
data=template_data,
output_name="status.png",
image_type="png"
)
try:
base64_str = await image_manager.render_template_to_base64(
template_name="status.html",
data=template_data,
output_name="status.png",
image_type="png"
)
if base64_str:
await event.reply(MessageSegment.image(base64_str))
else:
# 如果渲染失败image_manager 内部会记录错误,这里给用户一个通用提示
await event.reply("状态图片生成失败,可能是渲染服务出现问题,请联系管理员。")
if base64_str:
await event.reply(MessageSegment.image(base64_str))
else:
# 如果渲染失败image_manager 内部会记录错误,这里给用户一个通用提示
await event.reply("状态图片生成失败,可能是渲染服务出现问题,请联系管理员。")
except Exception as e:
logger.error(f"渲染图片失败: {e}")
await event.reply("状态图片渲染过程中发生错误。")
except Exception as e:
logger.exception(f"生成状态图时发生意外错误, 用户: {event.user_id}")