refactor: 将流循环管理器替换为调度器分发器以处理消息

- 引入SchedulerDispatcher以通过统一调度器管理消息分发。
- 更新了MessageManager以使用新的调度器,移除了旧的流循环管理功能。
- 增强了 add_message 方法,以便在接收消息时通知调度器。
- 已移除废弃的中断处理方法,将其逻辑整合至调度器中。
- 修改了action_manager,改为等待数据库操作而非使用asyncio.create_task以实现更精细的控制。
- 优化了统一调度器,支持并发任务执行及运行中任务的取消。
- 为重构流程和新架构添加了全面的文档说明。
This commit is contained in:
Windpicker-owo
2025-11-04 23:13:52 +08:00
parent ac964b9753
commit ca70ae1563
8 changed files with 906 additions and 885 deletions

View File

@@ -0,0 +1,210 @@
# 消息分发器重构文档
## 重构日期
2025-11-04
## 重构目标
将基于异步任务循环的消息分发机制改为使用统一的 `unified_scheduler`,实现更优雅和可维护的消息处理流程。
## 重构内容
### 1. 修改 unified_scheduler 以支持完全并发执行
**文件**: `src/schedule/unified_scheduler.py`
**主要改动**:
- 修改 `_check_and_trigger_tasks` 方法,使用 `asyncio.create_task` 为每个到期任务创建独立的异步任务
- 新增 `_execute_task_callback` 方法,用于并发执行单个任务
- 使用 `asyncio.gather` 并发等待所有任务完成,确保不同 schedule 之间完全异步执行,不会相互阻塞
**关键改进**:
```python
# 为每个任务创建独立的异步任务,确保并发执行
execution_tasks = []
for task in tasks_to_trigger:
execution_task = asyncio.create_task(
self._execute_task_callback(task, current_time),
name=f"execute_{task.task_name}"
)
execution_tasks.append(execution_task)
# 等待所有任务完成(使用 return_exceptions=True 避免单个任务失败影响其他任务)
results = await asyncio.gather(*execution_tasks, return_exceptions=True)
```
### 2. 创建新的 SchedulerDispatcher
**文件**: `src/chat/message_manager/scheduler_dispatcher.py`
**功能**:
基于 `unified_scheduler` 的消息分发器,替代原有的 `stream_loop_task` 循环机制。
**工作流程**:
1. **接收消息时**: 将消息添加到聊天流上下文(缓存)
2. **检查 schedule**: 查看该聊天流是否有活跃的 schedule
3. **打断判定**: 如果有活跃 schedule检查是否需要打断
- 如果需要打断,移除旧 schedule 并创建新的
- 如果不需要打断,保持原有 schedule
4. **创建 schedule**: 如果没有活跃 schedule创建新的
5. **Schedule 触发**: 当 schedule 到期时,激活 chatter 进行处理
6. **处理完成**: 计算下次间隔并根据需要注册新的 schedule
**关键方法**:
- `on_message_received(stream_id)`: 消息接收时的处理入口
- `_check_interruption(stream_id, context)`: 检查是否应该打断
- `_create_schedule(stream_id, context)`: 创建新的 schedule
- `_cancel_and_recreate_schedule(stream_id, context)`: 取消并重新创建 schedule
- `_on_schedule_triggered(stream_id)`: schedule 触发时的回调
- `_process_stream(stream_id, context)`: 激活 chatter 处理消息
### 3. 修改 MessageManager 集成新分发器
**文件**: `src/chat/message_manager/message_manager.py`
**主要改动**:
1. 导入 `scheduler_dispatcher`
2. 启动时初始化 `scheduler_dispatcher` 而非 `stream_loop_manager`
3. 修改 `add_message` 方法:
- 将消息添加到上下文后
- 调用 `scheduler_dispatcher.on_message_received(stream_id)` 处理消息接收事件
4. 废弃 `_check_and_handle_interruption` 方法(打断逻辑已集成到 dispatcher
**新的消息接收流程**:
```python
async def add_message(self, stream_id: str, message: DatabaseMessages):
# 1. 检查 notice 消息
if self._is_notice_message(message):
await self._handle_notice_message(stream_id, message)
if not global_config.notice.enable_notice_trigger_chat:
return
# 2. 将消息添加到上下文
chat_stream = await chat_manager.get_stream(stream_id)
await chat_stream.context_manager.add_message(message)
# 3. 通知 scheduler_dispatcher 处理
await scheduler_dispatcher.on_message_received(stream_id)
```
### 4. 更新模块导出
**文件**: `src/chat/message_manager/__init__.py`
**改动**:
- 导出 `SchedulerDispatcher``scheduler_dispatcher`
## 架构对比
### 旧架构 (基于 stream_loop_task)
```
消息到达 -> add_message -> 添加到上下文 -> 检查打断 -> 取消 stream_loop_task
-> 重新创建 stream_loop_task
stream_loop_task: while True:
检查未读消息 -> 处理消息 -> 计算间隔 -> sleep(间隔)
```
**问题**:
- 每个聊天流维护一个独立的异步循环任务
- 即使没有消息也需要持续轮询
- 打断逻辑通过取消和重建任务实现,较为复杂
- 难以统一管理和监控
### 新架构 (基于 unified_scheduler)
```
消息到达 -> add_message -> 添加到上下文 -> dispatcher.on_message_received
-> 检查是否有活跃 schedule
-> 打断判定
-> 创建/更新 schedule
schedule 到期 -> _on_schedule_triggered -> 处理消息 -> 计算间隔 -> 创建新 schedule (如果需要)
```
**优势**:
- 使用统一的调度器管理所有聊天流
- 按需创建 schedule没有消息时不会创建
- 打断逻辑清晰:移除旧 schedule + 创建新 schedule
- 易于监控和统计(统一的 scheduler 统计)
- 完全异步并发,多个 schedule 可以同时触发而不相互阻塞
## 兼容性
### 保留的组件
- `stream_loop_manager`: 暂时保留但不启动,以便需要时回滚
- `_check_and_handle_interruption`: 保留方法签名但不执行,避免破坏现有调用
### 移除的组件
- 无(本次重构采用渐进式方式,先添加新功能,待稳定后再移除旧代码)
## 配置项
所有配置项保持不变,新分发器完全兼容现有配置:
- `chat.interruption_enabled`: 是否启用打断
- `chat.allow_reply_interruption`: 是否允许回复时打断
- `chat.interruption_max_limit`: 最大打断次数
- `chat.distribution_interval`: 基础分发间隔
- `chat.force_dispatch_unread_threshold`: 强制分发阈值
- `chat.force_dispatch_min_interval`: 强制分发最小间隔
## 测试建议
1. **基本功能测试**
- 单个聊天流接收消息并正常处理
- 多个聊天流同时接收消息并并发处理
2. **打断测试**
- 在 chatter 处理过程中发送新消息,验证打断逻辑
- 验证打断次数限制
- 验证打断概率计算
3. **间隔计算测试**
- 验证基于能量的动态间隔计算
- 验证强制分发阈值触发
4. **并发测试**
- 多个聊天流的 schedule 同时到期,验证并发执行
- 验证不同 schedule 之间不会相互阻塞
5. **长时间稳定性测试**
- 运行较长时间,观察是否有内存泄漏
- 观察 schedule 创建和销毁是否正常
## 回滚方案
如果新机制出现问题,可以通过以下步骤回滚:
1.`message_manager.py``start()` 方法中:
```python
# 注释掉新分发器
# await scheduler_dispatcher.start()
# scheduler_dispatcher.set_chatter_manager(self.chatter_manager)
# 启用旧分发器
await stream_loop_manager.start()
stream_loop_manager.set_chatter_manager(self.chatter_manager)
```
2. 在 `add_message()` 方法中:
```python
# 注释掉新逻辑
# await scheduler_dispatcher.on_message_received(stream_id)
# 恢复旧逻辑
await self._check_and_handle_interruption(chat_stream, message)
```
3. 在 `_check_and_handle_interruption()` 方法中移除开头的 `return` 语句
## 后续工作
1. 在确认新机制稳定后,完全移除 `stream_loop_manager` 相关代码
2. 清理 `StreamContext` 中的 `stream_loop_task` 字段
3. 移除 `_check_and_handle_interruption` 方法
4. 更新相关文档和注释
## 性能预期
- **资源占用**: 减少(不再为每个流维护独立循环)
- **响应延迟**: 不变(仍基于相同的间隔计算)
- **并发能力**: 提升(完全异步执行,无阻塞)
- **可维护性**: 提升(逻辑更清晰,统一管理)