diff --git a/app/core/websocket/client.py b/app/core/websocket/client.py index 13af893..ac0a04b 100644 --- a/app/core/websocket/client.py +++ b/app/core/websocket/client.py @@ -50,6 +50,7 @@ class WebSocketClient: self._receive_task: Optional[asyncio.Task] = None self._created_at = datetime.now() self._last_message_at: Optional[datetime] = None + self.heartbeat_interval: Optional[int] = None # 心跳间隔配置 logger.info(f"创建WebSocket客户端: {name} -> {url}") diff --git a/app/core/websocket/manager.py b/app/core/websocket/manager.py index 6dee6c1..10a6bbd 100644 --- a/app/core/websocket/manager.py +++ b/app/core/websocket/manager.py @@ -57,11 +57,8 @@ class WebSocketManager: # 创建3个Channel await self._create_client_channels(name) - # 启动心跳任务 - 确保即使其他步骤失败也要尝试启动心跳 - try: - await self._start_heartbeat_task(name, heartbeat_interval) - except Exception as e: - logger.error(f"心跳任务启动失败,但继续创建客户端: {name} - {e}") + # 保存心跳间隔配置,在连接成功后启动 + client.heartbeat_interval = heartbeat_interval logger.info(f"WebSocket管理器创建客户端: {name} -> {url}") return client @@ -170,11 +167,15 @@ class WebSocketManager: logger.info(f"心跳循环启动成功: {client_name} -> {heartbeat_channel.name}") - while client_name in self._clients and self._clients[client_name].is_connected: + while client_name in self._clients: try: + client = self._clients[client_name] + is_connected = client.is_connected + # 创建心跳消息 from app.core.websocket.channel import ChannelMessage heartbeat_data = {"Message": "ping"} + heartbeat_message = ChannelMessage( type="heartbeat", data=heartbeat_data, @@ -282,7 +283,18 @@ class WebSocketManager: logger.error(f"WebSocket客户端 {name} 不存在") return False - return await client.connect() + # 连接客户端 + success = await client.connect() + + # 如果连接成功,启动心跳任务 + if success and hasattr(client, 'heartbeat_interval'): + try: + await self._start_heartbeat_task(name, client.heartbeat_interval) + logger.info(f"WebSocket管理器连接成功后启动心跳任务: {name}") + except Exception as e: + logger.error(f"心跳任务启动失败: {name} - {e}") + + return success async def disconnect_client(self, name: str) -> bool: """断开指定客户端""" diff --git a/modify.md b/modify.md index ca37795..9c3d84c 100644 --- a/modify.md +++ b/modify.md @@ -2,6 +2,68 @@ ## 2025-08-12 +### WebSocket心跳任务启动时机修复 +**问题**:心跳任务应该在客户端连接成功后才启动,而不是在创建客户端时就启动 + +**解决方案**: +1. 修改`create_client`方法,移除心跳任务启动逻辑 +2. 在`connect_client`方法中添加心跳任务启动逻辑 +3. 为WebSocketClient类添加heartbeat_interval属性 + +**文件变更**: +- 更新 `app/core/websocket/manager.py` - 修改心跳任务启动时机 +- 更新 `app/core/websocket/client.py` - 添加heartbeat_interval属性 + +**修改内容**: + +1. **create_client方法优化**: +```python +async def create_client(self, name: str, url: str, heartbeat_interval: int = 120) -> WebSocketClient: + # ... 创建客户端和Channel ... + + # 保存心跳间隔配置,在连接成功后启动 + client.heartbeat_interval = heartbeat_interval + + logger.info(f"WebSocket管理器创建客户端: {name} -> {url}") + return client +``` + +2. **connect_client方法增强**: +```python +async def connect_client(self, name: str) -> bool: + # 连接客户端 + success = await client.connect() + + # 如果连接成功,启动心跳任务 + if success and hasattr(client, 'heartbeat_interval'): + try: + await self._start_heartbeat_task(name, client.heartbeat_interval) + logger.info(f"WebSocket管理器连接成功后启动心跳任务: {name}") + except Exception as e: + logger.error(f"心跳任务启动失败: {name} - {e}") + + return success +``` + +3. **WebSocketClient类增强**: +```python +def __init__(self, url: str, name: str = "default"): + # ... 其他属性 ... + self.heartbeat_interval: Optional[int] = None # 心跳间隔配置 +``` + +**优化效果**: +- ✅ 心跳任务在连接成功后启动,避免无效的心跳 +- ✅ 心跳间隔配置保存在客户端对象中 +- ✅ 连接失败时不会启动心跳任务 +- ✅ 更好的资源管理和错误处理 + +**设计原则**: +- 心跳任务只在连接成功时启动 +- 心跳间隔配置与客户端绑定 +- 连接失败时不会产生无效的心跳任务 +- 保持清晰的职责分离 + ### 日志系统完全统一 - 移除冗余文件 **问题**:项目前期不需要向后兼容,`log.py` 文件造成冗余和混乱 - `get_enhanced_logger()` 只是简单包装了 `get_structured_logger()` @@ -1618,4 +1680,9 @@ client = await websocket_manager.create_client("test_1", "ws://localhost:8080", ### 测试建议 1. 创建WebSocket客户端并检查日志中是否出现心跳任务启动信息 2. 验证心跳消息是否正常发送 -3. 测试异常情况下的心跳任务启动 \ No newline at end of file +3. 测试异常情况下的心跳任务启动 + +### 补充说明 +- 心跳数据格式保持为 `{"Message": "ping"}`,与原有格式一致 +- 心跳循环现在会在客户端存在时持续运行,无论连接状态如何 +- 这样可以确保心跳机制在客户端创建后立即开始工作 \ No newline at end of file