Debian Node.js 日志中的并发问题解决方案
一 问题定位与日志增强
tail -f /var/log/nodejs/app.log | grep -i "error\|exception\|unhandled"。二 并发架构优化
pm2 start app.js -i max),充分利用多核;注意每个进程有独立内存空间,避免共享可变状态。三 关键代码示例
// 安装:npm i winston
const winston = require('winston');
const { v4: uuidv4 } = require('uuid');
const logger = winston.createLogger({
level: 'info',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.errors({ stack: true }),
winston.format.json()
),
transports: [new winston.transports.File({ filename: '/var/log/nodejs/app.log' })]
});
function requestLogger(req, res, next) {
req.traceId = req.headers['x-request-id'] || uuidv4();
const start = process.hrtime.bigint();
res.on('finish', () => {
const [sec, ns] = process.hrtime.bigint();
const durationMs = Number(sec - start) / 1e6;
logger.info('http_request', {
trace_id: req.traceId,
method: req.method,
url: req.url,
status: res.statusCode,
duration_ms: durationMs.toFixed(2),
pid: process.pid
});
});
next();
}
// 安装:npm i bottleneck
const Bottleneck = require('bottleneck');
// 例如:每秒 100 个请求,突发 50
const limiter = new Bottleneck({ maxConcurrent: 50, minTime: 10 });
app.get('/data', async (req, res) => {
try {
const result = await limiter.schedule(() => db.query('SELECT ...'));
res.json(result);
} catch (err) {
logger.error('db_error', { trace_id: req.traceId, err: err.stack });
res.status(500).json({ error: 'Internal Server Error' });
}
});
// app.js
const cluster = require('cluster');
const os = require('os');
if (cluster.isPrimary) {
const numCPUs = os.cpus().length;
for (let i = 0; i < numCPUs; i++) cluster.fork();
cluster.on('exit', (worker) => cluster.fork());
} else {
const express = require('express');
const app = express();
app.use(requestLogger);
// 为不同路由设置不同并发上限
app.get('/search', searchLimiter.middleware(), searchHandler);
app.get('/report', reportLimiter.middleware(), reportHandler);
app.listen(3000);
}
启动:pm2 start app.js -i max --name api。
四 监控 压测与持续优化