diff --git a/CRASH-ANALYSIS.md b/CRASH-ANALYSIS.md new file mode 100644 index 0000000..2bcaa76 --- /dev/null +++ b/CRASH-ANALYSIS.md @@ -0,0 +1,247 @@ +# 服务崩溃问题分析报告 + +## 问题现象 +- 时间: 2026-02-24 16:14 +- 症状: 服务无响应 ERR_EMPTY_RESPONSE +- 频率: 已发生2次(14:55, 16:14) + +## 根本原因分析 + +### 1. 缺少异常处理机制 (严重) 🔴 + +**问题代码**: +```javascript +// server.js 无全局异常捕获 +app.listen(PORT, () => { ... }); + +// 所有API路由无错误处理 +app.get('/api/stats', (req, res) => { + db.get('SELECT ...', (err, result) => { + // err未处理,直接使用result + res.json(result); + }); +}); +``` + +**风险**: +- 未捕获异常导致进程崩溃 +- 数据库错误导致服务器退出 +- 客户端请求错误无响应 + +**影响**: 任何未预期的错误都会导致整个服务崩溃 + +--- + +### 2. 进程管理缺失 (严重) 🔴 + +**当前启动方式**: +```bash +node server.js > server.log 2>&1 & +``` + +**问题**: +- ❌ 没有进程守护 +- ❌ 没有自动重启 +- ❌ 依赖shell会话(可能被终端关闭影响) +- ❌ 没有进程监控 + +**对比专业方案**: +```bash +# 应该使用 PM2 +pm2 start server.js --name task-service +pm2 startup # 开机自启 +pm2 save # 保存配置 +``` + +--- + +### 3. 数据库连接未处理超时 (中等) 🟡 + +**问题**: +```javascript +const db = new sqlite3.Database('./tasks.db', (err) => { + if (err) { + process.exit(1); // 直接退出 + } +}); +// 没有连接保活机制 +// 没有重连机制 +``` + +**风险**: +- 数据库文件锁定导致连接失败 +- 长时间运行后可能连接失效 + +--- + +### 4. 端口冲突无处理 (中等) 🟡 + +**问题**: +```javascript +app.listen(PORT, () => { ... }); +// 没有监听 'error' 事件 +``` + +**可能情况**: +``` +端口3000被占用 → listen失败 → 无错误提示 → 服务看似启动实际未运行 +``` + +--- + +### 5. 配置文件不匹配 (轻微) 🟢 + +**package.json**: +```json +{ + "main": "server-postgres.js", // ❌ 文件已删除 + "scripts": { + "start": "node server-postgres.js" // ❌ 错误 + } +} +``` + +**影响**: npm start 无法运行 + +--- + +## 推荐修复方案(优先级排序) + +### 🔴 P0 - 立即修复(必须) + +#### 1. 添加全局异常处理 +```javascript +// server.js 底部添加 +process.on('uncaughtException', (err) => { + console.error('❌ 未捕获异常:', err); + // 不退出,记录日志继续运行 +}); + +process.on('unhandledRejection', (reason, promise) => { + console.error('❌ 未处理的Promise拒绝:', reason); +}); + +// 监听端口错误 +const server = app.listen(PORT, () => { ... }); +server.on('error', (err) => { + if (err.code === 'EADDRINUSE') { + console.error(`❌ 端口 ${PORT} 已被占用`); + process.exit(1); + } +}); +``` + +#### 2. 添加API错误处理中间件 +```javascript +// 所有路由后添加 +app.use((err, req, res, next) => { + console.error('API错误:', err); + res.status(500).json({ error: err.message }); +}); +``` + +#### 3. 使用PM2管理进程 +```bash +npm install -g pm2 +pm2 start server.js --name task-service +pm2 startup +pm2 save +``` + +--- + +### 🟡 P1 - 重要优化(建议) + +#### 4. 数据库查询添加try-catch +```javascript +app.get('/api/stats', async (req, res) => { + try { + db.get('SELECT ...', (err, result) => { + if (err) { + return res.status(500).json({ error: err.message }); + } + res.json(result); + }); + } catch (error) { + res.status(500).json({ error: error.message }); + } +}); +``` + +#### 5. 添加健康检查端点 +```javascript +app.get('/health', (req, res) => { + res.json({ status: 'ok', timestamp: new Date() }); +}); +``` + +--- + +### 🟢 P2 - 长期改进(可选) + +#### 6. 日志系统升级 +```javascript +const winston = require('winston'); +// 结构化日志,自动轮转 +``` + +#### 7. 监控告警 +```javascript +// 性能监控 +// 崩溃告警 +// 自动重启通知 +``` + +--- + +## 临时解决方案(当前使用) + +```bash +# 1. 检查端口 +lsof -i :3000 + +# 2. 杀死占用进程 +pkill -f "node server" + +# 3. 重启服务 +cd /data/openclaw/workspace/agent-task-service +node server.js > server.log 2>&1 & + +# 4. 验证 +curl http://localhost:3000/api/stats +``` + +**缺点**: 不解决根本问题,随时可能再次崩溃 + +--- + +## 建议执行顺序 + +1. **立即** - 修复server.js添加异常处理 +2. **立即** - 安装并配置PM2 +3. **今天** - 添加API错误处理 +4. **本周** - 完善日志和监控 + +--- + +## 测试验证 + +修复后需验证: +```bash +# 1. 模拟崩溃 +curl http://localhost:3000/api/nonexist + +# 2. 检查进程存活 +pm2 status + +# 3. 查看日志 +pm2 logs task-service + +# 4. 压力测试 +ab -n 1000 -c 10 http://localhost:3000/ +``` + +--- + +生成时间: 2026-02-24 16:20 +优先级: P0 (生产环境严重问题) diff --git a/package.json b/package.json index 5953c63..b1ebe14 100644 --- a/package.json +++ b/package.json @@ -1,12 +1,15 @@ { "name": "agent-task-service", - "version": "1.0.0", + "version": "3.1.0", "description": "多智能体任务管理系统", - "main": "server-postgres.js", + "main": "server.js", "scripts": { - "start": "node server-postgres.js", - "dev": "node server-postgres.js", - "sqlite": "node server.js" + "start": "node server.js", + "dev": "node server.js", + "pm2": "pm2 start server.js --name task-service", + "pm2:stop": "pm2 stop task-service", + "pm2:restart": "pm2 restart task-service", + "pm2:logs": "pm2 logs task-service" }, "keywords": [ "multi-agent", diff --git a/server.js b/server.js index 1aebd9e..33e08d5 100644 --- a/server.js +++ b/server.js @@ -87,6 +87,17 @@ db.serialize(() => { // ==================== API 路由 ==================== +// 健康检查 +app.get('/health', (req, res) => { + res.json({ + status: 'ok', + timestamp: new Date().toISOString(), + uptime: process.uptime(), + memory: process.memoryUsage(), + version: require('./package.json').version + }); +}); + // 统计信息 app.get('/api/stats', (req, res) => { const stats = {}; @@ -342,18 +353,66 @@ app.delete('/api/rules/:id', (req, res) => { }); +// 错误处理中间件(必须在所有路由之后) +app.use((err, req, res, next) => { + console.error('❌ API错误:', err.stack); + res.status(500).json({ + error: err.message || '服务器内部错误', + timestamp: new Date().toISOString() + }); +}); + // 启动服务器 -app.listen(PORT, () => { +const server = app.listen(PORT, () => { console.log(`🚀 多智能体任务管理系统已启动`); console.log(` 本地访问: http://localhost:${PORT}`); console.log(` 远程访问: http://10.0.6.5:${PORT}`); }); +// 监听端口错误 +server.on('error', (err) => { + if (err.code === 'EADDRINUSE') { + console.error(`❌ 端口 ${PORT} 已被占用`); + console.error(` 请先停止占用端口的进程: lsof -ti:${PORT} | xargs kill -9`); + process.exit(1); + } else { + console.error('❌ 服务器启动失败:', err); + process.exit(1); + } +}); + +// 全局异常捕获 +process.on('uncaughtException', (err) => { + console.error('❌ 未捕获异常:', err); + console.error(' 堆栈:', err.stack); + // 不退出进程,继续运行 +}); + +process.on('unhandledRejection', (reason, promise) => { + console.error('❌ 未处理的Promise拒绝:', reason); + console.error(' Promise:', promise); + // 不退出进程,继续运行 +}); + // 优雅关闭 process.on('SIGINT', () => { - db.close((err) => { - if (err) console.error(err); - console.log('\n👋 服务器已关闭'); - process.exit(0); + console.log('\n⏸️ 收到关闭信号,准备关闭服务器...'); + server.close(() => { + console.log('📡 HTTP服务器已关闭'); + db.close((err) => { + if (err) { + console.error('❌ 数据库关闭失败:', err); + process.exit(1); + } + console.log('💾 数据库连接已关闭'); + console.log('👋 服务器已完全关闭'); + process.exit(0); + }); }); + + // 强制超时退出(10秒) + setTimeout(() => { + console.error('⚠️ 强制退出(超时)'); + process.exit(1); + }, 10000); }); diff --git a/service.sh b/service.sh new file mode 100755 index 0000000..d6afbd3 --- /dev/null +++ b/service.sh @@ -0,0 +1,62 @@ +#!/bin/bash +# 任务管理系统服务控制脚本 + +PM2="/data/npm-global/bin/pm2" +SERVICE_NAME="task-service" +SERVICE_DIR="/data/openclaw/workspace/agent-task-service" + +case "$1" in + start) + echo "🚀 启动服务..." + cd "$SERVICE_DIR" + $PM2 start server.js --name "$SERVICE_NAME" + ;; + stop) + echo "⏸️ 停止服务..." + $PM2 stop "$SERVICE_NAME" + ;; + restart) + echo "🔄 重启服务..." + $PM2 restart "$SERVICE_NAME" + ;; + status) + $PM2 status "$SERVICE_NAME" + ;; + logs) + $PM2 logs "$SERVICE_NAME" --lines ${2:-50} + ;; + monitor) + $PM2 monit + ;; + health) + echo "🏥 健康检查..." + curl -s http://localhost:3000/health | python3 -m json.tool + ;; + test) + echo "🧪 测试API..." + echo "1. 健康检查:" + curl -s http://localhost:3000/health | python3 -m json.tool | head -5 + echo "" + echo "2. 统计信息:" + curl -s http://localhost:3000/api/stats | python3 -m json.tool + ;; + *) + echo "用法: $0 {start|stop|restart|status|logs|monitor|health|test}" + echo "" + echo "命令说明:" + echo " start - 启动服务" + echo " stop - 停止服务" + echo " restart - 重启服务" + echo " status - 查看状态" + echo " logs - 查看日志(可选参数:行数,默认50)" + echo " monitor - 实时监控" + echo " health - 健康检查" + echo " test - 测试API" + echo "" + echo "示例:" + echo " $0 logs 100 # 查看最近100行日志" + exit 1 + ;; +esac + +exit 0