feat(反向WS管理): 实现多前端支持与完善清理机制

- 为每个前端创建独立的Bot实例,防止状态混乱
- 分离消息锁和时间戳存储,修复清理逻辑错误
- 完善客户端断开时的清理逻辑,包括负载计数和健康状态
- 添加文档说明多前端支持的功能和解决方案
This commit is contained in:
2026-02-28 21:20:20 +08:00
parent b5e08833e0
commit 0baf07a716
2 changed files with 204 additions and 3 deletions

175
MULTI_FRONTEND_SUPPORT.md Normal file
View File

@@ -0,0 +1,175 @@
# 多前端支持问题分析和解决方案
## 已实现的功能
### 1. 负载均衡
- ✅ 自动选择负载最低的健康客户端
- ✅ 健康检查30秒内有活动
- ✅ 负载计数(消息处理次数)
- ✅ API调用时自动切换客户端
### 2. 防重复发送
- ✅ 事件ID检查`id`/`post_id`/`time`
- ✅ 消息锁机制(异步锁)
- ✅ 双重检查(锁内外各一次)
- ✅ 自动清理过期数据
### 3. 多前端支持
- ✅ 每个前端独立的Bot实例
- ✅ 客户端连接/断开管理
- ✅ 完整的清理机制
## 已修复的问题
### 1. 清理不完整
**问题**:客户端断开连接时没有清理负载计数和健康状态
**解决方案**
```python
async def _disconnect_client(self, client_id: str) -> None:
if client_id in self.clients:
del self.clients[client_id]
if client_id in self.client_self_ids:
del self.client_self_ids[client_id]
if client_id in self._client_load:
del self._client_load[client_id]
if client_id in self._client_health:
del self._client_health[client_id]
if client_id in self.bots:
del self.bots[client_id]
```
### 2. Bot实例共享
**问题**多个前端共享同一个Bot实例可能导致状态混乱
**解决方案**为每个前端创建独立的Bot实例
```python
# Bot实例字典每个前端独立的Bot实例
self.bots: Dict[str, Bot] = {}
# 为每个前端创建独立的Bot实例
if client_id not in self.bots:
temp_ws = WS()
temp_ws.self_id = event.self_id if hasattr(event, 'self_id') else 0
self.bots[client_id] = Bot(temp_ws)
event.bot = self.bots[client_id]
```
### 3. 清理逻辑错误
**问题**清理过期数据时将Lock对象当作时间戳来处理
**解决方案**分离存储Lock对象和时间戳
```python
# 分离存储
self._message_locks: Dict[str, asyncio.Lock] = {} # 存储Lock对象
self._message_lock_times: Dict[str, datetime] = {} # 存储Lock的创建时间
# 清理时使用时间戳字典
expired_locks = [
lock_key for lock_key, timestamp in self._message_lock_times.items()
if (current_time - timestamp).total_seconds() > self._lock_ttl
]
```
## 可能存在的问题
### 1. API响应混淆
**问题描述**:当多个前端同时响应时,`_pending_requests`是全局共享的,可能导致响应匹配错误
**当前解决方案**使用echo_id匹配响应任何前端的响应都会被正确匹配
```python
echo_id = data.get("echo")
if echo_id and echo_id in self._pending_requests:
future = self._pending_requests.pop(echo_id)
if not future.done():
future.set_result(data)
```
**潜在风险**如果多个前端同时响应同一个API请求只有第一个响应会被处理
**建议优化**可以考虑在API调用时指定客户端ID确保响应来自正确的客户端
### 2. 负载均衡不准确
**问题描述**:负载计数可能不准确,因为多个前端可能同时处理相同的消息
**当前解决方案**:负载计数只是参考,系统会优先选择健康的客户端
**建议优化**:可以考虑使用更复杂的负载均衡策略,如加权轮询
### 3. Bot实例状态
**问题描述**每个前端有独立的Bot实例可能导致状态不一致
**当前解决方案**Bot实例是无状态的只依赖于WS实例和self_id
**建议优化**如果需要共享状态可以使用Redis等外部存储
## 最佳实践
### 1. 部署建议
- 部署2-3个前端实例进行负载均衡
- 确保前端实例之间的网络连接稳定
- 定期检查前端连接状态
### 2. 监控建议
- 关注重复事件日志,排查网络问题
- 监控客户端健康状态
- 监控API调用成功率
### 3. 调试建议
- 使用`get_healthy_clients()`查看健康客户端
- 使用`get_client_with_least_load()`查看负载最低的客户端
- 查看日志了解API调用情况
## 性能优化建议
### 1. API响应过滤
可以在API调用时记录客户端ID确保响应来自正确的客户端
```python
# 记录API请求的客户端ID
self._api_requests[echo_id] = {
'client_id': client_id,
'timestamp': datetime.now()
}
# 处理响应时验证客户端ID
if echo_id in self._api_requests:
request_info = self._api_requests[echo_id]
if request_info['client_id'] == client_id:
# 处理响应
```
### 2. 负载均衡策略
可以考虑使用更复杂的负载均衡策略:
- 加权轮询:根据客户端性能分配权重
- 最少连接:选择连接数最少的客户端
- 响应时间:选择响应时间最短的客户端
### 3. 缓存优化
可以考虑使用Redis缓存Bot实例的状态
```python
# 缓存Bot实例的状态
await redis_manager.set(f"neobot:bot:{client_id}:status", status_data, ex=300)
```
## 故障排查
### 问题:消息重复处理
**原因**:网络延迟导致前端重复发送
**解决**系统已自动处理检查事件ID是否正确设置
### 问题API调用超时
**原因**:选择的客户端不健康或网络问题
**解决**:系统会自动切换到其他健康客户端
### 问题:所有客户端都不健康
**原因**:前端断开连接或网络问题
**解决**:检查前端连接状态和网络连接
### 问题Bot实例初始化失败
**原因**WS实例缺失或self_id未设置
**解决**确保在事件处理时正确设置self_id