186513f381
- 前端消息流式逐字渲染 (AI-Core ChatStream → SSE → Gateway → WebSocket stream_chunk → fadeInUp + cursorBlink) - 后端对话缓存 (conversationCache sync.Map, GET /sessions/:id/messages) - 前端侧边栏历史多轮对话显示 - DevTools 性能监控图标移至首页仪表盘 - DevTools 用户记忆查询/删减功能修复 (补全 DELETE 数据链路) - 后端和 DevTools 按用户分类组织实时活动会话 (map[userID]map[sessionID]*Client) - 新增 docs/api-reference/ 路由参考文档 - 新增 docs/message-flow-architecture.md 消息链路架构文档
166 lines
4.4 KiB
JavaScript
166 lines
4.4 KiB
JavaScript
/**
|
|
* 性能监控模块
|
|
* 监控各服务进程的 CPU、内存使用情况
|
|
*/
|
|
|
|
import pidusage from 'pidusage';
|
|
import { processManager } from './process-manager.js';
|
|
import { SERVICES } from './config.js';
|
|
|
|
class PerformanceMonitor {
|
|
constructor() {
|
|
/** @type {Map<string, Array<{ts: number, cpu: number, mem: number}>>} */
|
|
this.history = new Map();
|
|
this.interval = null;
|
|
|
|
for (const id of Object.keys(SERVICES)) {
|
|
this.history.set(id, []);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 开始定期采样 (每3秒)
|
|
*/
|
|
start() {
|
|
if (this.interval) return;
|
|
this.interval = setInterval(() => this.sample(), 3000);
|
|
this.interval.unref(); // 不阻止进程退出
|
|
}
|
|
|
|
/**
|
|
* 停止采样
|
|
*/
|
|
stop() {
|
|
if (this.interval) {
|
|
clearInterval(this.interval);
|
|
this.interval = null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 采样一次
|
|
*/
|
|
async sample() {
|
|
for (const [id, info] of processManager.processes) {
|
|
if (!info.pid) continue;
|
|
try {
|
|
const stats = await pidusage(info.pid);
|
|
const history = this.history.get(id);
|
|
history.push({
|
|
ts: Date.now(),
|
|
cpu: Math.round(stats.cpu * 100) / 100,
|
|
mem: Math.round(stats.memory / 1024 / 1024 * 100) / 100, // MB
|
|
});
|
|
// 保留最近300条 (约15分钟)
|
|
if (history.length > 300) {
|
|
history.splice(0, history.length - 300);
|
|
}
|
|
} catch {
|
|
// 进程可能已退出
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 获取当前性能快照
|
|
*/
|
|
async getSnapshot() {
|
|
const result = {};
|
|
for (const [id, info] of processManager.processes) {
|
|
if (!info.pid) {
|
|
result[id] = { pid: null, cpu: 0, mem: 0 };
|
|
continue;
|
|
}
|
|
try {
|
|
const stats = await pidusage(info.pid);
|
|
result[id] = {
|
|
pid: info.pid,
|
|
cpu: Math.round(stats.cpu * 100) / 100,
|
|
mem: Math.round(stats.memory / 1024 / 1024 * 100) / 100,
|
|
elapsed: stats.elapsed,
|
|
};
|
|
} catch {
|
|
result[id] = { pid: info.pid, cpu: 0, mem: 0 };
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* 获取历史数据
|
|
*/
|
|
getHistory(serviceId) {
|
|
return this.history.get(serviceId) || [];
|
|
}
|
|
|
|
/**
|
|
* 获取所有服务的历史数据
|
|
*/
|
|
getAllHistory() {
|
|
const result = {};
|
|
for (const id of this.history.keys()) {
|
|
result[id] = this.history.get(id);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* 更新仪表盘数据 — 返回聚合的性能摘要供首页仪表盘使用
|
|
* 调用方负责将数据渲染到 #performance-dashboard 元素
|
|
* @returns {object} 仪表盘性能摘要
|
|
*/
|
|
async updateDashboard() {
|
|
const snapshot = await this.getSnapshot();
|
|
const entries = Object.entries(snapshot);
|
|
|
|
let totalCpu = 0, totalMem = 0, activeCount = 0;
|
|
for (const [, p] of entries) {
|
|
totalCpu += p.cpu || 0;
|
|
totalMem += p.mem || 0;
|
|
if (p.pid) activeCount++;
|
|
}
|
|
|
|
const avgCpu = entries.length > 0 ? Math.round(totalCpu / entries.length * 10) / 10 : 0;
|
|
const totalMemRounded = Math.round(totalMem * 100) / 100;
|
|
|
|
// 计算平均延迟 (基于各服务进程的 elapsed 时间)
|
|
let avgLatencyMs = null;
|
|
let totalElapsed = 0, elapsedCount = 0;
|
|
for (const [, p] of entries) {
|
|
if (p.elapsed && p.elapsed > 0) { totalElapsed += p.elapsed; elapsedCount++; }
|
|
}
|
|
if (elapsedCount > 0) {
|
|
avgLatencyMs = Math.round(totalElapsed / elapsedCount);
|
|
}
|
|
|
|
// 获取最近历史用于趋势判断
|
|
const recentHistory = this.getAllHistory();
|
|
let trendCpu = 'stable', trendMem = 'stable';
|
|
for (const [, hist] of Object.entries(recentHistory)) {
|
|
if (hist.length < 5) continue;
|
|
const recent = hist.slice(-5);
|
|
const firstCpu = recent[0].cpu, lastCpu = recent[recent.length - 1].cpu;
|
|
const firstMem = recent[0].mem, lastMem = recent[recent.length - 1].mem;
|
|
if (lastCpu > firstCpu * 1.15) trendCpu = 'up';
|
|
else if (lastCpu < firstCpu * 0.85) trendCpu = 'down';
|
|
if (lastMem > firstMem * 1.15) trendMem = 'up';
|
|
else if (lastMem < firstMem * 0.85) trendMem = 'down';
|
|
}
|
|
|
|
return {
|
|
timestamp: Date.now(),
|
|
summary: {
|
|
avgCpu,
|
|
totalMemMB: totalMemRounded,
|
|
activeProcesses: activeCount,
|
|
monitoredServices: entries.length,
|
|
avgLatencyMs,
|
|
trend: { cpu: trendCpu, mem: trendMem },
|
|
},
|
|
perService: snapshot,
|
|
};
|
|
}
|
|
}
|
|
|
|
export const performanceMonitor = new PerformanceMonitor();
|