fix(system-monitor): 改进进程CPU使用率计算准确性和稳定性

- 优先使用Node.js内置的process.cpuUsage()计算当前进程CPU使用率,提高跨平台准确性
- 为外部进程ps命令添加错误处理,避免在BusyBox环境下报错干扰日志
- 更新Dockerfile安装procps工具以支持系统监控功能
- 同步更新版本号至2.11.1
This commit is contained in:
hex2077 2026-03-09 19:05:41 +08:00
parent f2f02b365f
commit 7d2704b14e
3 changed files with 40 additions and 11 deletions

View file

@ -19,8 +19,8 @@ FROM node:20-alpine
LABEL maintainer="AIClient2API Team" LABEL maintainer="AIClient2API Team"
LABEL description="Docker image for AIClient2API server" LABEL description="Docker image for AIClient2API server"
# 安装必要的系统工具tar 用于更新功能git 用于版本检查 # 安装必要的系统工具tar 用于更新功能git 用于版本检查procps 用于系统监控
RUN apk add --no-cache tar git RUN apk add --no-cache tar git procps
# 设置工作目录 # 设置工作目录
WORKDIR /app WORKDIR /app

View file

@ -1 +1 @@
2.11.0 2.11.1

View file

@ -57,8 +57,31 @@ export function getProcessCpuUsagePercent(pid) {
const isWindows = process.platform === 'win32'; const isWindows = process.platform === 'win32';
let cpuPercent = 0; let cpuPercent = 0;
if (isWindows) { // 优先处理当前进程,使用 Node.js 内置的 process.cpuUsage(),这在所有平台上都更准确且无需外部命令
// Windows 下使用 PowerShell 获取进程的 CPU 使用率 if (pid === process.pid) {
const usage = process.cpuUsage();
const timestamp = Date.now();
const totalMicroseconds = usage.user + usage.system;
const prevInfo = processCpuInfoMap.get(pid);
if (prevInfo && prevInfo.totalMicroseconds !== undefined) {
const timeDiff = (timestamp - prevInfo.timestamp) * 1000; // 转换为微秒
const processTimeDiff = totalMicroseconds - prevInfo.totalMicroseconds;
if (timeDiff > 0) {
const cpuCount = os.cpus().length;
cpuPercent = (processTimeDiff / timeDiff) * 100;
// Node.js 返回的是所有核心累加的使用量,归一化到 0-100%
cpuPercent = cpuPercent / cpuCount;
}
}
processCpuInfoMap.set(pid, {
totalMicroseconds,
timestamp
});
} else if (isWindows) {
// Windows 下使用 PowerShell 获取其他进程的 CPU 使用率
// CPU = (Process.TotalProcessorTime / ElapsedTime) / ProcessorCount // CPU = (Process.TotalProcessorTime / ElapsedTime) / ProcessorCount
const command = `powershell -Command "Get-Process -Id ${pid} | Select-Object -ExpandProperty TotalProcessorTime | ForEach-Object { $_.TotalSeconds }"`; const command = `powershell -Command "Get-Process -Id ${pid} | Select-Object -ExpandProperty TotalProcessorTime | ForEach-Object { $_.TotalSeconds }"`;
const output = execSync(command, { encoding: 'utf8' }).trim(); const output = execSync(command, { encoding: 'utf8' }).trim();
@ -67,7 +90,7 @@ export function getProcessCpuUsagePercent(pid) {
if (!isNaN(totalProcessorSeconds)) { if (!isNaN(totalProcessorSeconds)) {
const prevInfo = processCpuInfoMap.get(pid); const prevInfo = processCpuInfoMap.get(pid);
if (prevInfo) { if (prevInfo && prevInfo.totalProcessorSeconds !== undefined) {
const timeDiff = (timestamp - prevInfo.timestamp) / 1000; // 转换为秒 const timeDiff = (timestamp - prevInfo.timestamp) / 1000; // 转换为秒
const processTimeDiff = totalProcessorSeconds - prevInfo.totalProcessorSeconds; const processTimeDiff = totalProcessorSeconds - prevInfo.totalProcessorSeconds;
@ -85,11 +108,17 @@ export function getProcessCpuUsagePercent(pid) {
}); });
} }
} else { } else {
// Linux/macOS 使用 ps 命令直接获取 // Linux/macOS 使用 ps 命令获取其他进程的 CPU 使用率
const output = execSync(`ps -p ${pid} -o %cpu`, { encoding: 'utf8' }); // 增加 2> /dev/null 以防在 BusyBox 等环境下报错干扰日志
const lines = output.trim().split('\n'); try {
if (lines.length >= 2) { const output = execSync(`ps -p ${pid} -o %cpu 2>/dev/null`, { encoding: 'utf8' });
cpuPercent = parseFloat(lines[1].trim()); const lines = output.trim().split('\n');
if (lines.length >= 2) {
cpuPercent = parseFloat(lines[1].trim());
}
} catch (e) {
// 如果 ps -p 失败,尝试更通用的 ps 方式或直接忽略
cpuPercent = 0;
} }
} }