30 KiB
AI 实现指导提示词
任务概述
你需要实现将任务队列后端的 runninghub 任务优先分发到内部 message-dispatcher 系统的功能。请按照以下详细指导进行修改。
一、任务队列后端项目修改指导
项目路径
d:\Ke_xue_web\独立项目\comfyui桥接器\任务队列后端\
修改点 0:新增 WebSocket 通信模块
文件: utils/mdWebSocketClient.js(新建)
通信架构说明:
- message-dispatcher 主动通过 WebSocket 连接任务队列后端
- WebSocket 用于:
- JWT Token 接收与定期更新
- 算力状态上报
- 实例状态变化同步
- 健康检查
- 仅任务提交通过 HTTP 接口完成
- 任务提交接口 URL 通过环境变量获取,与 runninghub 保持一致
- 所有其他通信(包括 Token 获取)均通过 WebSocket
功能要求:
- 作为 WebSocket 服务端,等待 message-dispatcher 连接
- 接收 JWT Token 消息(JWT_UPDATE)
- 接收算力状态更新消息(CAPACITY_UPDATE)
- 接收实例状态变化消息(INSTANCE_ONLINE/INSTANCE_OFFLINE 等)
- 发送心跳响应
- 提供 Token、算力状态查询接口
核心方法:
class MDWebSocketServer {
constructor() {
this.wss = null;
this.connectedClients = new Map();
this.currentJwtToken = null;
this.currentCapacity = { internal: 0, external: 0 };
this.instances = new Map();
}
// 初始化并启动 WebSocket 服务
async init()
// 获取当前 JWT Token
getJwtToken()
// 获取当前内部算力可用数
getInternalCapacity()
// 获取当前外部容量
getExternalCapacity()
// 获取所有实例状态
getInstances()
// 检查是否有连接的客户端
hasConnectedClients()
}
WebSocket 消息处理:
// JWT Token 更新
handleJwtUpdate(data) {
this.currentJwtToken = data.token;
console.log('[MDWebSocketServer] JWT Token 已更新');
}
// 算力状态更新
handleCapacityUpdate(data) {
this.currentCapacity.internal = data.summary.onlineInstances - data.summary.busyInstances;
}
// 实例上线
handleInstanceOnline(data) {
this.instances.set(data.instanceId, { ...data, status: 'online' });
}
// 实例下线
handleInstanceOffline(data) {
this.instances.set(data.instanceId, { ...data, status: 'offline' });
}
// 心跳响应
handleHeartbeat(data, ws) {
ws.send(JSON.stringify({
type: 'HEARTBEAT_ACK',
data: { timestamp: new Date().toISOString() }
}));
}
WebSocket 消息格式:
// message-dispatcher 发送的 JWT 更新消息
{
type: 'JWT_UPDATE',
data: {
token: 'eyJhbGciOiJIUzI1NiIs...',
expiresAt: '2024-01-02T00:00:00.000Z',
timestamp: '2024-01-01T00:00:00.000Z'
}
}
// message-dispatcher 发送的心跳
{
type: 'HEARTBEAT',
data: {
timestamp: '2024-01-01T00:00:00.000Z'
}
}
修改点 1:新增 messageDispatcher 平台适配器
文件: outside/outPlatforms/messageDispatcher.js(新建)
核心原则:
- 与 runninghub.js 保持完全一致的接口方法签名
- 仅修改请求地址和 apiKey 字段的值
- 请求体所有字段名称、类型、格式与 runninghub 完全一致
- 任务提交通过 HTTP 接口,URL 通过环境变量获取
要求:
- 参考
runninghub.js的接口设计,保持方法签名完全一致 - 实现以下 6 个核心方法:
getGenerateUrl() // 返回 message-dispatcher 的任务提交接口(从环境变量获取)
getGenerateHeader(apikey) // 返回请求头(包含 JWT Token 作为 apiKey)
getGenerateBody(task) // 构造请求体(与 runninghub 完全一致)
getSuccessTasks(response) // 处理成功响应,转换为 runninghub 兼容格式
getTaskResult(response) // 处理结果回调
getQueryUrl() // 返回回调地址(与 runninghub 保持一致)
请求体标准化规则:
| 字段名称 | 类型 | 是否修改 | 说明 |
|---|---|---|---|
workflow_id |
String | 否 | 原样保留 |
node_info_list |
Array | 否 | 原样保留 |
apiKey |
String | 是 | 值改为 JWT Token |
webhookUrl |
String | 否 | 原样保留 |
| 其他所有字段 | - | 否 | 原样保留 |
请求体示例对比:
// runninghub 请求体(原样)
{
"workflow_id": "123",
"node_info_list": [...],
"apiKey": "runninghub-api-key-xxx",
"webhookUrl": "http://callback-url"
}
// message-dispatcher 请求体(仅修改 apiKey 值)
{
"workflow_id": "123",
"node_info_list": [...],
"apiKey": "eyJhbGciOiJIUzI1NiIs...", // JWT Token
"webhookUrl": "http://callback-url"
}
响应转换规则:
// message-dispatcher 响应
{ success: true, data: { requestId: "xxx" } }
// 转换为
{ msg: "success", code: 0, data: { taskId: "xxx" } }
修改点 2:简化 JWT Token 获取(不再需要独立模块)
重要说明:
- 不再需要独立的 JWTManager 模块
- JWT Token 由 message-dispatcher 通过 WebSocket 主动推送
- Token 定期更新也通过 WebSocket 推送
- 从 MDWebSocketServer 获取 Token 即可
Token 获取方式:
// 从 WebSocket 服务端获取当前 Token
const jwtToken = mdWebSocketServer.getJwtToken();
修改点 3:新增任务分流模块
文件: utils/taskDistributor.js(新建)
功能要求:
- 移除高低优先级区分
- 实现统一的任务分流逻辑
- 从 WebSocket 服务端获取实时容量信息
- 根据容量进行任务分配
核心计算公式:
内部算力可用数 = 从 MDWebSocketClient 获取
外部容量上限 = 从配置/环境变量获取(如 10)
总可分发任务上限 = 内部算力可用数 + 外部容量上限
分流策略:
| 待分发任务数 | 内部容量 | 外部容量 | 分配结果 |
|---|---|---|---|
| ≤ 内部 | 实时 | 10 | 全部走内部 |
| > 内部 ≤ 总 | 实时 | 10 | 前N内部,超出部分走外部 |
| > 总 | 实时 | 10 | 内部 + 外部,剩余等待 |
具体示例:
示例 1:
内部 = 30(实时), 外部 = 10, 待分发 = 25
结果:全部 25 个走内部
示例 2:
内部 = 30(实时), 外部 = 10, 待分发 = 35
结果:30 个内部,5 个外部
示例 3:
内部 = 30(实时), 外部 = 10, 待分发 = 45
结果:30 个内部,10 个外部,5 个等待
核心实现:
async function distributeTasks(tasks, mdWebSocketServer) {
const internalCapacity = mdWebSocketServer.getInternalCapacity();
const externalCapacity = await getExternalCapacityFromConfig();
const internalTasks = tasks.slice(0, internalCapacity);
const externalTasks = tasks.slice(internalCapacity, internalCapacity + externalCapacity);
const remainingTasks = tasks.slice(internalCapacity + externalCapacity);
return { internalTasks, externalTasks, remainingTasks };
}
修改点 4:修改任务分发逻辑(批量)
文件: worker_threads/wait/waiting.js
修改位置: 任务批量获取和分发逻辑
要求:
- 引入 MDWebSocketServer
- 在获取任务后,先调用 taskDistributor 进行分流(传入 WebSocket 服务端)
- 根据分流结果分别分发到内部/外部
- 剩余任务返回队列
修改点 5:修改任务分发逻辑(单个)
文件: outside/generat.js
修改位置: externalPostRequest() 函数
决策逻辑:
if (platform === 'runninghub') {
if (使用内部算力) {
尝试使用 messageDispatcher 平台发送任务
if (成功) {
返回内部结果
} else {
记录降级日志
降级使用 runninghub
}
} else {
使用 runninghub
}
} else {
使用原平台
}
关键实现:
- 引入 MDWebSocketServer(从这里获取 JWT Token)
- 引入 messageDispatcher 平台适配器
- 增加降级日志记录
- 保持原有错误处理逻辑不变
修改点 6:更新平台管理
文件: outside/outPlatforms/outside.js
修改内容:
- 导入 messageDispatcher 模块
- 将其添加到导出对象中
import * as runninghub from './runninghub.js';
import * as jimuai from './JimuAI.js';
import coze from './coze/coze.js';
import * as messageDispatcher from './messageDispatcher.js'; // 新增
export default { runninghub, jimuai, coze, messageDispatcher }; // 新增
修改点 7:更新环境变量配置
文件: .env
新增配置:
# Message Dispatcher 配置
MESSAGE_DISPATCHER_URL=http://localhost:4000
MESSAGE_DISPATCHER_WS_PORT=8087
MESSAGE_DISPATCHER_ENABLED=true
MESSAGE_DISPATCHER_TIMEOUT=30000
# 外部容量配置
EXTERNAL_CAPACITY_MAX=10
说明:
MESSAGE_DISPATCHER_URL:HTTP 任务提交接口 URLMESSAGE_DISPATCHER_WS_PORT:任务队列后端 WebSocket 服务端口- 不再需要 MD_USERNAME 和 MD_PASSWORD(Token 通过 WebSocket 推送)
修改点 9:新增配置文件
文件: config/messageDispatcher.json(新建)
{
"enabled": true,
"priority": true,
"task": {
"timeout": 30000,
"retryCount": 1
},
"capacity": {
"external": 10
},
"websocket": {
"port": 8087
}
}
修改点 10:算力更新逻辑检查与修复
文件: worker_threads/wait/waiting.js、worker_threads/callback_result/result.js、redis/initQueue.js 等相关文件
核心需求:
- 详细检查任务状态管理与算力更新相关代码
- 验证 30 个任务未全部完成,收到算力更新通知的场景
- 实现未用算力数计算边界检查,防止负值
- 添加防御性编程,避免空转或无限循环
- 实现完整单元测试
9.1 检查清单
| 检查项 | 检查文件 | 风险级别 |
|---|---|---|
| 算力计数更新原子性 | redis/initQueue.js |
高 |
| 任务完成回调时算力计数 | worker_threads/callback_result/result.js |
高 |
| 算力减少时的任务处理 | worker_threads/wait/waiting.js |
高 |
| 负数检查与防御 | 所有相关文件 | 高 |
| 并发安全 | 所有相关文件 | 中 |
9.2 核心修复方案
修复 1:添加算力计数边界检查
文件: redis/initQueue.js
问题: 任务完成后,PQtasks(处理中任务数)可能减为负数
修复代码:
async function reducePlatformsProcess(platformKey) {
const key = `${prefix}:platforms:${platformKey}`;
try {
const current = await redis.hGet(key, 'PQtasks');
let newValue = parseInt(current) - 1;
// 边界检查:确保不小于 0
if (newValue < 0) {
console.warn(`[CapacityManager] 检测到负值: ${platformKey} PQtasks = ${newValue}, 已修正为 0`);
newValue = 0;
}
await redis.hSet(key, 'PQtasks', newValue.toString());
console.log(`[CapacityManager] ${platformKey} PQtasks: ${current} -> ${newValue}`);
return newValue;
} catch (error) {
console.error(`[CapacityManager] 更新 PQtasks 失败:`, error);
throw error;
}
}
修复 2:添加算力更新状态锁
文件: utils/capacityGuard.js(新建)
功能: 防止算力更新期间的并发问题
class CapacityGuard {
constructor() {
this.updateLock = false;
this.pendingUpdates = [];
}
async acquireLock() {
while (this.updateLock) {
await new Promise(resolve => setTimeout(resolve, 10));
}
this.updateLock = true;
}
releaseLock() {
this.updateLock = false;
// 处理排队的更新
if (this.pendingUpdates.length > 0) {
const nextUpdate = this.pendingUpdates.shift();
nextUpdate();
}
}
async executeWithLock(fn) {
await this.acquireLock();
try {
return await fn();
} finally {
this.releaseLock();
}
}
}
export default new CapacityGuard();
修复 3:处理算力突然降低场景
文件: worker_threads/wait/waiting.js
场景: 已发送 30 个任务,算力降低至 20,任务未全部完成
修复代码:
async function handleCapacityReductionFromMD(newInternalCapacity) {
console.log(`[Waiting] 收到算力更新: 内部容量 -> ${newInternalCapacity}`);
await capacityGuard.executeWithLock(async () => {
// 获取当前正在处理的任务数
const currentProcessing = await getCurrentProcessingCount();
if (currentProcessing <= newInternalCapacity) {
console.log(`[Waiting] 当前处理数 ${currentProcessing} ≤ 新容量 ${newInternalCapacity}, 无需调整`);
return;
}
const excess = currentProcessing - newInternalCapacity;
console.warn(`[Waiting] 检测到算力降低: 当前处理 ${currentProcessing} > 新容量 ${newInternalCapacity}, 超出 ${excess} 个任务`);
// 记录超出情况,但不主动取消任务
// 让任务自然完成,通过回调正确更新计数
console.log(`[Waiting] 将等待任务自然完成,确保计数正确`);
});
}
修复 4:添加空转防御
文件: worker_threads/wait/waiting.js
问题: 无任务时可能无限循环
修复代码:
// 主循环
(async () => {
let idleCount = 0;
const MAX_IDLE_COUNT = 10; // 最大连续空转次数
const IDLE_SLEEP_MS = 10000; // 空转时的睡眠时间
while (true) {
try {
const wDeficiency = await judgConcurrency();
if (wDeficiency.length > 0) {
idleCount = 0; // 重置空转计数
logger.info('有可进行处理的队列,数量: ' + wDeficiency.length);
// ... 原有处理逻辑 ...
} else {
idleCount++;
if (idleCount >= MAX_IDLE_COUNT) {
logger.debug(`连续空转 ${idleCount} 次,进入长睡眠`);
await new Promise(resolve => setTimeout(resolve, IDLE_SLEEP_MS));
idleCount = 0;
} else {
logger.debug('没有可处理的队列');
await new Promise(resolve => setTimeout(resolve, 10000));
}
}
} catch (error) {
logger.error('批量处理任务失败:', error);
await new Promise(resolve => setTimeout(resolve, 5000));
}
}
})();
9.3 单元测试方案
测试文件: test/capacity.test.js(新建)
测试用例:
describe('Capacity Management Tests', () => {
test('正常情况: 任务完成后算力正确增加', async () => {
await initQueue.addPlatformsProcess({ 'digitalHuman:runninghub': 1 });
const result = await initQueue.reducePlatformsProcess('digitalHuman:runninghub');
assert.strictEqual(result, 0);
});
test('边界情况: 算力为0时尝试减少', async () => {
await initQueue.addPlatformsProcess({ 'digitalHuman:runninghub': 0 });
const result = await initQueue.reducePlatformsProcess('digitalHuman:runninghub');
assert.strictEqual(result, 0, '应该保持为0');
});
test('边界情况: 算力从30降低到20时的处理', async () => {
// 模拟发送30个任务
for (let i = 0; i < 30; i++) {
await initQueue.addPlatformsProcess({ 'digitalHuman:runninghub': 1 });
}
// 模拟收到算力降低通知
await handleCapacityReductionFromMD(20);
// 验证不会导致负数
const currentPQtasks = await getCurrentPQtasks();
assert(currentPQtasks >= 0, 'PQtasks 不能为负数');
});
test('防御性测试: 并发更新', async () => {
const promises = [];
for (let i = 0; i < 100; i++) {
promises.push(initQueue.addPlatformsProcess({ 'digitalHuman:runninghub': 1 }));
promises.push(initQueue.reducePlatformsProcess('digitalHuman:runninghub'));
}
await Promise.all(promises);
const finalCount = await getCurrentPQtasks();
assert(finalCount >= 0, '并发更新后不能为负数');
});
test('防御性测试: 空转检测', async () => {
const startTime = Date.now();
// 模拟无任务场景
await simulateIdleLoop();
const duration = Date.now() - startTime;
assert(duration < 60000, '不应该无限循环');
});
});
9.4 验证检查清单
| 验证项 | 验证方法 | 预期结果 |
|---|---|---|
| 算力不出现负值 | 检查日志中是否有负值警告 | 如有警告,确认已自动修正为 0 |
| 任务完成后计数正确 | 发送 10 个任务,全部完成 | 最终 PQtasks = 0 |
| 算力降低时不崩溃 | 发送 30 个任务,降低算力到 20 | 系统稳定运行,无错误 |
| 无任务时不空转 | 监控无任务时的 CPU | 进入睡眠,不占用 CPU |
| 并发更新安全 | 100 次并发增减 | 最终计数正确,无负值 |
二、message-dispatcher 项目修改指导
项目路径
d:\Ke_xue_web\独立项目\comfyui桥接器\message-dispatcher\
修改点 1:新增 WebSocket 客户端模块
文件: src/md-websocket-client/index.js(新建)
通信架构说明:
- message-dispatcher 作为 WebSocket 客户端,主动连接任务队列后端
- WebSocket 用于:
- JWT Token 主动推送与定期更新
- 算力状态上报
- 实例状态变化同步
- 心跳保活
- 仅任务提交通过 HTTP 接口完成
- 所有其他通信(包括 Token 推送)均通过 WebSocket
功能要求:
- 建立与任务队列后端的 WebSocket 连接
- 连接成功后立即推送当前 JWT Token
- 定期推送 JWT Token 更新(如每 20 小时)
- 定期推送算力状态(CAPACITY_UPDATE)
- 实例状态变化时推送(INSTANCE_ONLINE/INSTANCE_OFFLINE 等)
- 发送心跳保持连接
- 自动重连机制(指数退避)
核心方法:
class MDWebSocketClient {
constructor() {
this.ws = null;
this.connected = false;
this.reconnectAttempts = 0;
this.tokenPushInterval = null;
this.capacityPushInterval = null;
}
// 初始化并连接
async init()
// 连接到任务队列后端
async connect()
// 断开连接
disconnect()
// 推送 JWT Token
pushJwtToken()
// 推送算力状态
pushCapacityState()
// 推送实例上线
pushInstanceOnline(instanceId)
// 推送实例下线
pushInstanceOffline(instanceId)
// 发送消息
send(message)
// 处理接收到的消息
handleMessage(message)
}
WebSocket 消息格式(message-dispatcher 发送):
// JWT Token 更新推送
{
type: 'JWT_UPDATE',
data: {
token: 'eyJhbGciOiJIUzI1NiIs...',
expiresAt: '2024-01-02T00:00:00.000Z',
timestamp: '2024-01-01T00:00:00.000Z'
}
}
// 算力状态更新推送
{
type: 'CAPACITY_UPDATE',
data: {
timestamp: '2024-01-01T00:00:00.000Z',
bridges: [...],
summary: {
totalBridges: 2,
totalInstances: 8,
onlineInstances: 6,
busyInstances: 2,
availableCapacity: 4
}
}
}
// 心跳
{
type: 'HEARTBEAT',
data: {
timestamp: '2024-01-01T00:00:00.000Z'
}
}
修改点 2:修改启动流程,集成 WebSocket 客户端
文件: src/index.js
修改内容:
- 导入并初始化 MDWebSocketClient
- 在服务启动后启动 WebSocket 客户端
- 在服务关闭时断开 WebSocket 连接
修改点 3:新增 runninghub 兼容接口(可选)
目标: 确保接口兼容性,新增 runninghub 兼容的任务提交接口
文件: src/api/index.js
新增接口(可选):
// 兼容 runninghub 格式的任务提交接口
router.post('/task/runninghub', authMiddleware, async (req, res) => {
// 请求体已经是 runninghub 格式,直接使用
// 调用现有 /api/task 逻辑
});
修改点 4:确保回调兼容
说明: message-dispatcher 已支持 webhookUrl 参数,任务完成后会调用该回调。需要确保回调格式与 runninghub 保持一致。
检查点:
- 确认
TASK_END消息处理中正确调用 webhookUrl - 确保回调数据格式与 runninghub 兼容
修改点 5:新增任务处理与算力动态调整机制
文件: src/task-scheduler/index.js(新建)
核心需求:
- 当任务队列后端发送 30 个任务,而系统算力突然降低至 20 时,实现任务保留机制
- 将超出当前算力的任务存入缓存队列
- 实时监控算力变化,有空闲算力时按 FIFO 取出任务处理
- 管理缓存任务状态(等待中、处理中、已完成、失败重试等)
数据结构设计:
| 数据结构 | 类型 | 说明 |
|---|---|---|
pendingTaskQueue |
Array | 待执行任务缓存队列(FIFO) |
processingTasks |
Map | 执行中任务 |
completedTasks |
List | 已完成任务(最近 1000 条) |
failedTasks |
List | 失败任务(最近 100 条) |
任务状态定义:
const TASK_STATES = {
PENDING: 'pending', // 等待中
PROCESSING: 'processing', // 处理中
COMPLETED: 'completed', // 已完成
FAILED: 'failed', // 失败
RETRYING: 'retrying' // 重试中
};
核心类设计:
class TaskScheduler {
constructor() {
this.pendingTaskQueue = []; // FIFO 队列
this.processingTasks = new Map(); // taskId -> taskInfo
this.currentCapacity = 0; // 当前可用算力
this.maxCapacity = 0; // 最大算力
this.schedulerLoop = null;
}
// 初始化调度器
async init()
// 设置当前可用算力
setCurrentCapacity(capacity)
// 添加任务到缓存队列
addTaskToPending(task)
// 从缓存队列取出任务
getTaskFromPending()
// 将任务标记为处理中
markTaskAsProcessing(taskId, instanceId)
// 将任务标记为已完成
markTaskAsCompleted(taskId, result)
// 将任务标记为失败
markTaskAsFailed(taskId, error)
// 主调度循环
async schedulerLoop()
// 检查是否有空闲算力
hasAvailableCapacity()
// 获取可用任务数
getAvailableSlots()
// 处理算力降低
handleCapacityReduction(newCapacity)
// 处理算力增加
handleCapacityIncrease(newCapacity)
}
场景处理示例:
场景 1:算力从 30 降低至 20
async handleCapacityReduction(newCapacity) {
const currentProcessingCount = this.processingTasks.size;
// 如果处理中的任务数超过新容量
if (currentProcessingCount > newCapacity) {
const excessCount = currentProcessingCount - newCapacity;
// 获取最早开始的 excessCount 个任务
const tasksToMoveBack = Array.from(this.processingTasks.values())
.sort((a, b) => a.startTime - b.startTime)
.slice(0, excessCount);
// 将任务移回 pending 队列头部(优先级高)
for (const task of tasksToMoveBack.reverse()) {
this.processingTasks.delete(task.taskId);
this.pendingTaskQueue.unshift({
...task,
state: TASK_STATES.PENDING,
movedBackAt: new Date().toISOString()
});
}
console.log(`[TaskScheduler] 算力降低: ${this.currentCapacity} -> ${newCapacity}, 已将 ${excessCount} 个任务移回缓存队列`);
}
this.currentCapacity = newCapacity;
}
场景 2:有空闲算力时调度任务
async schedulePendingTasks() {
const availableSlots = this.getAvailableSlots();
if (availableSlots <= 0 || this.pendingTaskQueue.length === 0) {
return;
}
const tasksToSchedule = this.pendingTaskQueue.splice(0, availableSlots);
for (const task of tasksToSchedule) {
// 分配任务到可用实例
const instanceId = await this.selectAvailableInstance();
this.markTaskAsProcessing(task.taskId, instanceId);
// 发送任务到实例
await this.sendTaskToInstance(task, instanceId);
}
console.log(`[TaskScheduler] 已调度 ${tasksToSchedule.length} 个任务`);
}
算力更新监听:
// 监听来自任务队列后端的算力更新(通过 WebSocket)
handleCapacityUpdateFromBackend(data) {
const newCapacity = data.summary.availableCapacity;
if (newCapacity < this.currentCapacity) {
this.handleCapacityReduction(newCapacity);
} else if (newCapacity > this.currentCapacity) {
this.handleCapacityIncrease(newCapacity);
} else {
this.currentCapacity = newCapacity;
}
}
修改点 6:集成任务调度器到主流程
文件: src/index.js
修改内容:
- 导入并初始化 TaskScheduler
- 在 WebSocket 客户端收到算力更新时通知调度器
- 在任务开始/完成时通知调度器更新状态
- 在服务关闭时优雅关闭调度器
三、技术要求
3.1 代码规范
- 遵循现有代码风格(ES Module, async/await)
- 保持与 runninghub.js 相同的接口签名
- 请求体字段名称、类型、格式必须与 runninghub 完全一致
- 仅修改 apiKey 字段的值为 JWT Token
- 添加充分的日志记录(使用 console.log/console.error)
3.2 错误处理
- 健康检查失败不应导致主进程崩溃
- 降级机制必须可靠
- 超时处理完善
- JWT Token 更新失败不应中断服务
3.3 性能要求
- 健康检查间隔不小于 10 秒
- 决策时间不超过 100ms
- 不影响现有系统吞吐量
四、验收标准
4.1 功能验收
- 任务分流逻辑正确:≤内部容量全部走内部,超出部分走外部
- message-dispatcher 不可用时自动降级至 runninghub
- 请求体与 runninghub 完全一致(仅 apiKey 值不同)
- JWT Token 自动更新机制正常工作
- 任务成功执行并返回正确结果
- 回调接口正常工作
- 现有功能不受影响(jimuai、coze 等平台正常工作)
4.2 接口兼容性验收
- messageDispatcher.js 接口方法签名与 runninghub.js 完全一致
- 请求体字段名称、类型、格式与 runninghub 完全一致
- 响应格式与 runninghub 兼容
- 不修改 runninghub.js 的任何代码
4.3 容量边界验收
- WebSocket 服务正常启动,message-dispatcher 成功连接
- JWT Token 成功通过 WebSocket 接收
- JWT Token 定期更新通过 WebSocket 接收
- 算力状态实时同步(通过 WebSocket)
- 实例状态变化实时同步(通过 WebSocket)
- 场景 1:25个任务 → 全部25个走内部(实时容量)
- 场景 2:30个任务 → 全部30个走内部(实时容量)
- 场景 3:35个任务 → 30个内部,5个外部(实时容量)
- 场景 4:40个任务 → 30个内部,10个外部(实时容量)
- 场景 5:45个任务 → 30个内部,10个外部,5个等待(实时容量)
4.4 日志验收
- 记录每次分发决策(使用内部/外部)
- 记录容量使用统计
- 记录降级事件及原因
- 记录 JWT Token 接收/更新日志(通过 WebSocket)
- 记录 WebSocket 连接/断开日志
4.5 可靠性验收
- message-dispatcher 重启后自动恢复
- 网络波动不影响降级逻辑
- JWT Token 自动更新不中断服务
- 连续运行 24 小时无崩溃
4.6 算力管理验收
- 算力计数从不出现负值
- 任务完成后算力计数正确增加
- 算力从 30 降低到 20 时系统稳定
- 无任务时进入睡眠,不空转
- 100 次并发更新后计数正确
- 单元测试覆盖正常、边界、异常情况
五、关键文件参考
任务队列后端
outside/outPlatforms/runninghub.js- 参考接口设计outside/generat.js- 修改任务分发逻辑outside/outPlatforms/outside.js- 平台注册worker_threads/wait/waiting.js- 批量任务分流worker_threads/wait/generatTask.js- 任务处理流程
message-dispatcher
src/api/index.js- API 接口定义src/bridge-manager/index.js- 桥接器管理src/websocket-server/index.js- 任务发送逻辑
六、实现顺序建议
任务队列后端实现顺序
- 第一步: 创建
mdWebSocketServer.jsWebSocket 服务模块 - 第二步: 创建
taskDistributor.js任务分流模块 - 第三步: 创建
messageDispatcher.js平台适配器 - 第四步: 修改
waiting.js实现批量任务分流 - 第五步: 修改
generat.js实现单任务分发决策 - 第六步: 更新
outside.js和配置文件 - 第七步: 检查并修复
redis/initQueue.js算力计数(添加边界检查) - 第八步: 创建
capacityGuard.js算力更新锁 - 第九步: 修改
waiting.js添加空转防御 - 第十步: 创建单元测试
test/capacity.test.js - 第十一步: 测试验证功能
message-dispatcher 实现顺序
- 第一步: 创建
md-websocket-client/index.jsWebSocket 客户端模块 - 第二步: 修改
src/index.js集成 WebSocket 客户端 - 第三步: 创建
task-scheduler/index.js任务调度器 - 第四步: 修改
src/index.js集成任务调度器 - 第五步: 添加 runninghub 兼容接口(可选)
- 第六步: 确保回调兼容
- 第七步: 测试验证功能
七、注意事项
⚠️ 重要提醒:
- 不要修改
runninghub.js的现有代码 - 保持
externalPostRequest()的返回值格式不变 - 确保 请求体与 runninghub 完全一致(仅 apiKey 值不同)
- 确保 回调接口格式与 runninghub 完全一致
- 不要 破坏现有其他平台(jimuai、coze)的功能
- 移除 所有高低优先级任务的区分逻辑
- 新增 日志时使用清晰的前缀,如
[MessageDispatcher]、[JWTManager]、[TaskDistributor]
八、验证方法
8.1 接口兼容性验证
验证步骤:
- 对比 messageDispatcher.js 与 runninghub.js 的方法签名
- 验证 getGenerateBody() 返回的请求体字段名称
- 验证 getGenerateBody() 返回的请求体字段类型
- 确认仅 apiKey 字段的值被修改
验证代码:
// 对比两个平台适配器的接口
const runninghubMethods = Object.keys(runninghub);
const messageDispatcherMethods = Object.keys(messageDispatcher);
assert.deepEqual(runninghubMethods, messageDispatcherMethods, '接口方法必须一致');
8.2 任务分流验证
验证场景:
- 构造不同数量的待分发任务
- 检查内部/外部任务分配比例
- 验证不超过各自容量上限
现在,请按照以上指导开始实现!