Node.js 故障排除指南
更新: 8/8/2025 字数: 0 字 时长: 0 分钟
本章将介绍 Node.js 应用开发和部署过程中常见问题的诊断和解决方法。
常见错误类型
1. 语法错误 (Syntax Errors)
javascript
// 常见语法错误示例
// 1. 缺少括号或大括号
if (condition {
console.log('Missing closing parenthesis');
}
// SyntaxError: Unexpected token '{'
// 2. 缺少逗号
const obj = {
name: 'John'
age: 30 // 缺少逗号
};
// SyntaxError: Unexpected token 'age'
// 3. 字符串引号不匹配
const message = "Hello World';
// SyntaxError: Unterminated string constant
// 4. 非法的变量名
const 123name = 'invalid';
// SyntaxError: Unexpected number
// 解决方法:
// 1. 使用代码编辑器的语法高亮
// 2. 使用 ESLint 进行代码检查
// 3. 仔细检查括号、引号的匹配
2. 引用错误 (Reference Errors)
javascript
// 常见引用错误
// 1. 使用未声明的变量
console.log(undeclaredVariable);
// ReferenceError: undeclaredVariable is not defined
// 2. 在声明前使用变量(let/const)
console.log(myVar);
let myVar = 'Hello';
// ReferenceError: Cannot access 'myVar' before initialization
// 3. 模块导入错误
const nonExistentModule = require('./non-existent');
// Error: Cannot find module './non-existent'
// 解决方法:
// 1. 检查变量名拼写
// 2. 确保变量在使用前已声明
// 3. 检查模块路径是否正确
// 4. 使用严格模式捕获更多错误
'use strict';
// 调试技巧
const debugVariables = () => {
console.log('Available variables:', Object.keys(global));
console.log('Local scope:', Object.keys(this));
};
3. 类型错误 (Type Errors)
javascript
// 常见类型错误
// 1. 调用非函数
const notAFunction = 'Hello';
notAFunction();
// TypeError: notAFunction is not a function
// 2. 访问 null/undefined 的属性
const obj = null;
console.log(obj.property);
// TypeError: Cannot read property 'property' of null
// 3. 数组方法用于非数组
const notArray = 'string';
notArray.push('item');
// TypeError: notArray.push is not a function
// 防御性编程解决方案
class TypeSafeUtils {
// 安全的属性访问
static safeGet(obj, path, defaultValue = null) {
try {
return path.split('.').reduce((current, key) => {
return current && current[key] !== undefined ? current[key] : defaultValue;
}, obj);
} catch (error) {
return defaultValue;
}
}
// 类型检查
static isFunction(value) {
return typeof value === 'function';
}
static isArray(value) {
return Array.isArray(value);
}
static isObject(value) {
return value !== null && typeof value === 'object' && !Array.isArray(value);
}
static isString(value) {
return typeof value === 'string';
}
static isNumber(value) {
return typeof value === 'number' && !isNaN(value);
}
// 安全的函数调用
static safeCall(fn, ...args) {
if (this.isFunction(fn)) {
try {
return fn(...args);
} catch (error) {
console.error('Function call failed:', error.message);
return null;
}
}
console.warn('Attempted to call non-function:', typeof fn);
return null;
}
// 类型转换
static toNumber(value, defaultValue = 0) {
const num = Number(value);
return isNaN(num) ? defaultValue : num;
}
static toString(value, defaultValue = '') {
return value != null ? String(value) : defaultValue;
}
static toArray(value) {
if (this.isArray(value)) return value;
if (value == null) return [];
return [value];
}
}
// 使用示例
const data = { user: { profile: { name: 'John' } } };
const userName = TypeSafeUtils.safeGet(data, 'user.profile.name', 'Unknown');
const invalidName = TypeSafeUtils.safeGet(data, 'user.profile.age', 0);
console.log(userName); // 'John'
console.log(invalidName); // 0
异步编程问题
1. 回调地狱和错误处理
javascript
// 问题:回调地狱
const fs = require('fs');
// 错误的嵌套回调
fs.readFile('file1.txt', (err1, data1) => {
if (err1) throw err1;
fs.readFile('file2.txt', (err2, data2) => {
if (err2) throw err2;
fs.readFile('file3.txt', (err3, data3) => {
if (err3) throw err3;
console.log('All files read');
});
});
});
// 解决方案1:Promise 化
const { promisify } = require('util');
const readFileAsync = promisify(fs.readFile);
const readMultipleFiles = async () => {
try {
const [data1, data2, data3] = await Promise.all([
readFileAsync('file1.txt'),
readFileAsync('file2.txt'),
readFileAsync('file3.txt')
]);
console.log('All files read');
return { data1, data2, data3 };
} catch (error) {
console.error('Error reading files:', error.message);
throw error;
}
};
// 解决方案2:错误优先的回调处理
class CallbackHandler {
static handleCallback(callback) {
return (error, result) => {
if (error) {
console.error('Callback error:', error.message);
if (typeof callback === 'function') {
callback(error, null);
}
return;
}
if (typeof callback === 'function') {
callback(null, result);
}
};
}
static promisifyCallback(fn) {
return (...args) => {
return new Promise((resolve, reject) => {
fn(...args, (error, result) => {
if (error) {
reject(error);
} else {
resolve(result);
}
});
});
};
}
}
// 使用示例
const readFilePromise = CallbackHandler.promisifyCallback(fs.readFile);
readFilePromise('package.json', 'utf8')
.then(data => console.log('File content:', data))
.catch(error => console.error('Read error:', error.message));
2. Promise 和 async/await 错误
javascript
// 常见 Promise 错误
// 1. 忘记返回 Promise
const badPromiseChain = () => {
return fetch('/api/data')
.then(response => {
// 忘记返回,导致下一个 then 接收到 undefined
response.json();
})
.then(data => {
console.log(data); // undefined
});
};
// 正确的做法
const goodPromiseChain = () => {
return fetch('/api/data')
.then(response => response.json()) // 返回 Promise
.then(data => {
console.log(data); // 正确的数据
return data;
});
};
// 2. 未处理的 Promise 拒绝
const unhandledRejection = () => {
Promise.reject(new Error('Unhandled error'));
// 这会导致 UnhandledPromiseRejectionWarning
};
// 正确处理
const handledRejection = () => {
Promise.reject(new Error('Handled error'))
.catch(error => {
console.error('Caught error:', error.message);
});
};
// 3. async/await 错误处理
const asyncErrorHandling = async () => {
try {
// 并行执行多个异步操作
const [result1, result2, result3] = await Promise.allSettled([
fetch('/api/endpoint1'),
fetch('/api/endpoint2'),
fetch('/api/endpoint3')
]);
// 检查每个结果
const processResults = (results) => {
return results.map((result, index) => {
if (result.status === 'fulfilled') {
return { success: true, data: result.value, index };
} else {
console.error(`Request ${index + 1} failed:`, result.reason.message);
return { success: false, error: result.reason.message, index };
}
});
};
return processResults([result1, result2, result3]);
} catch (error) {
console.error('Unexpected error:', error.message);
throw error;
}
};
// Promise 超时处理
class PromiseUtils {
static timeout(promise, ms) {
return Promise.race([
promise,
new Promise((_, reject) => {
setTimeout(() => {
reject(new Error(`Operation timed out after ${ms}ms`));
}, ms);
})
]);
}
static retry(fn, maxAttempts = 3, delay = 1000) {
return new Promise(async (resolve, reject) => {
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
try {
const result = await fn();
resolve(result);
return;
} catch (error) {
console.warn(`Attempt ${attempt} failed:`, error.message);
if (attempt === maxAttempts) {
reject(new Error(`All ${maxAttempts} attempts failed. Last error: ${error.message}`));
return;
}
// 等待后重试
await new Promise(resolve => setTimeout(resolve, delay * attempt));
}
}
});
}
static async batchProcess(items, processor, batchSize = 5) {
const results = [];
for (let i = 0; i < items.length; i += batchSize) {
const batch = items.slice(i, i + batchSize);
try {
const batchResults = await Promise.all(
batch.map(item => processor(item))
);
results.push(...batchResults);
} catch (error) {
console.error(`Batch ${Math.floor(i / batchSize) + 1} failed:`, error.message);
// 可以选择继续处理其他批次或抛出错误
throw error;
}
}
return results;
}
}
// 使用示例
const fetchWithTimeout = async (url) => {
return PromiseUtils.timeout(fetch(url), 5000);
};
const fetchWithRetry = async (url) => {
return PromiseUtils.retry(() => fetch(url), 3, 1000);
};
内存问题
1. 内存泄漏检测和修复
javascript
// 内存泄漏检测工具
class MemoryMonitor {
constructor() {
this.initialMemory = process.memoryUsage();
this.snapshots = [];
}
// 获取当前内存使用情况
getCurrentMemory() {
const usage = process.memoryUsage();
return {
rss: Math.round(usage.rss / 1024 / 1024), // MB
heapTotal: Math.round(usage.heapTotal / 1024 / 1024),
heapUsed: Math.round(usage.heapUsed / 1024 / 1024),
external: Math.round(usage.external / 1024 / 1024)
};
}
// 创建内存快照
takeSnapshot(label = '') {
const snapshot = {
label,
timestamp: new Date().toISOString(),
memory: this.getCurrentMemory()
};
this.snapshots.push(snapshot);
console.log(`Memory snapshot [${label}]:`, snapshot.memory);
return snapshot;
}
// 比较内存使用
compareSnapshots(snapshot1, snapshot2) {
const diff = {
rss: snapshot2.memory.rss - snapshot1.memory.rss,
heapTotal: snapshot2.memory.heapTotal - snapshot1.memory.heapTotal,
heapUsed: snapshot2.memory.heapUsed - snapshot1.memory.heapUsed,
external: snapshot2.memory.external - snapshot1.memory.external
};
console.log(`Memory diff [${snapshot1.label} -> ${snapshot2.label}]:`, diff);
return diff;
}
// 监控内存增长
startMonitoring(interval = 10000) {
this.monitoringInterval = setInterval(() => {
const current = this.getCurrentMemory();
// 检查内存增长
if (this.snapshots.length > 0) {
const last = this.snapshots[this.snapshots.length - 1];
const growth = current.heapUsed - last.memory.heapUsed;
if (growth > 50) { // 50MB 增长警告
console.warn(`Memory growth detected: +${growth}MB`);
}
}
this.takeSnapshot('monitoring');
// 保持最近 10 个快照
if (this.snapshots.length > 10) {
this.snapshots.shift();
}
}, interval);
}
stopMonitoring() {
if (this.monitoringInterval) {
clearInterval(this.monitoringInterval);
this.monitoringInterval = null;
}
}
// 强制垃圾回收(需要 --expose-gc 标志)
forceGC() {
if (global.gc) {
console.log('Forcing garbage collection...');
global.gc();
return this.takeSnapshot('after-gc');
} else {
console.warn('Garbage collection not exposed. Run with --expose-gc flag.');
return null;
}
}
}
// 常见内存泄漏模式和修复
// 1. 事件监听器泄漏
class EventLeakExample {
constructor() {
this.listeners = new Map();
}
// 错误:没有清理监听器
badEventHandling() {
const emitter = new EventEmitter();
// 这些监听器永远不会被移除
emitter.on('data', (data) => {
console.log('Received:', data);
});
return emitter;
}
// 正确:管理监听器生命周期
goodEventHandling() {
const emitter = new EventEmitter();
const dataHandler = (data) => {
console.log('Received:', data);
};
emitter.on('data', dataHandler);
// 保存引用以便后续清理
this.listeners.set('dataHandler', { emitter, handler: dataHandler, event: 'data' });
return emitter;
}
// 清理所有监听器
cleanup() {
for (const [name, { emitter, handler, event }] of this.listeners) {
emitter.removeListener(event, handler);
console.log(`Removed listener: ${name}`);
}
this.listeners.clear();
}
}
// 2. 定时器泄漏
class TimerLeakExample {
constructor() {
this.timers = new Set();
}
// 错误:没有清理定时器
badTimerHandling() {
setInterval(() => {
console.log('This timer will never be cleared');
}, 1000);
}
// 正确:管理定时器
goodTimerHandling() {
const timerId = setInterval(() => {
console.log('Managed timer');
}, 1000);
this.timers.add(timerId);
return timerId;
}
clearTimer(timerId) {
if (this.timers.has(timerId)) {
clearInterval(timerId);
this.timers.delete(timerId);
console.log('Timer cleared');
}
}
clearAllTimers() {
for (const timerId of this.timers) {
clearInterval(timerId);
}
this.timers.clear();
console.log('All timers cleared');
}
}
// 3. 闭包泄漏
class ClosureLeakExample {
// 错误:闭包持有大对象引用
badClosureHandling() {
const largeData = new Array(1000000).fill('data');
return function() {
// 即使不使用 largeData,闭包仍然持有引用
console.log('Function called');
};
}
// 正确:避免不必要的闭包引用
goodClosureHandling() {
const processLargeData = (data) => {
// 处理数据后立即释放引用
const result = data.length;
data = null; // 显式释放
return result;
};
const largeData = new Array(1000000).fill('data');
const result = processLargeData(largeData);
return function() {
console.log('Result:', result);
};
}
}
// 使用示例
const memoryMonitor = new MemoryMonitor();
memoryMonitor.startMonitoring(5000);
// 在应用关闭时清理
process.on('SIGINT', () => {
console.log('Cleaning up...');
memoryMonitor.stopMonitoring();
process.exit(0);
});
2. 大文件处理
javascript
// 大文件处理最佳实践
const fs = require('fs');
const { pipeline } = require('stream');
const { promisify } = require('util');
const pipelineAsync = promisify(pipeline);
class LargeFileHandler {
// 错误:一次性读取大文件
static badLargeFileHandling(filePath) {
// 这会将整个文件加载到内存中
return fs.readFileSync(filePath, 'utf8');
}
// 正确:使用流处理大文件
static async goodLargeFileHandling(inputPath, outputPath, processor) {
const readStream = fs.createReadStream(inputPath, { encoding: 'utf8' });
const writeStream = fs.createWriteStream(outputPath);
// 创建转换流
const transformStream = new require('stream').Transform({
transform(chunk, encoding, callback) {
try {
const processed = processor(chunk.toString());
callback(null, processed);
} catch (error) {
callback(error);
}
}
});
try {
await pipelineAsync(readStream, transformStream, writeStream);
console.log('File processed successfully');
} catch (error) {
console.error('File processing failed:', error.message);
throw error;
}
}
// 分块读取大文件
static async readFileInChunks(filePath, chunkSize = 1024 * 1024) {
const chunks = [];
const readStream = fs.createReadStream(filePath, {
highWaterMark: chunkSize
});
return new Promise((resolve, reject) => {
readStream.on('data', (chunk) => {
chunks.push(chunk);
// 可以在这里处理每个块
console.log(`Read chunk of size: ${chunk.length}`);
});
readStream.on('end', () => {
console.log(`Total chunks read: ${chunks.length}`);
resolve(chunks);
});
readStream.on('error', reject);
});
}
// CSV 文件逐行处理
static async processCSVFile(filePath, processor) {
const readline = require('readline');
const fileStream = fs.createReadStream(filePath);
const rl = readline.createInterface({
input: fileStream,
crlfDelay: Infinity // 处理 Windows 换行符
});
let lineNumber = 0;
const results = [];
try {
for await (const line of rl) {
lineNumber++;
try {
const result = await processor(line, lineNumber);
if (result !== null) {
results.push(result);
}
} catch (error) {
console.error(`Error processing line ${lineNumber}:`, error.message);
// 可以选择继续或停止处理
}
// 每处理 1000 行报告进度
if (lineNumber % 1000 === 0) {
console.log(`Processed ${lineNumber} lines`);
}
}
console.log(`Total lines processed: ${lineNumber}`);
return results;
} catch (error) {
console.error('CSV processing failed:', error.message);
throw error;
}
}
// 监控文件处理进度
static async processFileWithProgress(filePath, processor) {
const stats = fs.statSync(filePath);
const totalSize = stats.size;
let processedSize = 0;
const readStream = fs.createReadStream(filePath);
readStream.on('data', (chunk) => {
processedSize += chunk.length;
const progress = (processedSize / totalSize * 100).toFixed(2);
// 更新进度(可以发送到客户端)
process.stdout.write(`\rProgress: ${progress}%`);
});
return new Promise((resolve, reject) => {
let result = '';
readStream.on('data', (chunk) => {
result += processor(chunk.toString());
});
readStream.on('end', () => {
console.log('\nFile processing completed');
resolve(result);
});
readStream.on('error', reject);
});
}
}
// 使用示例
const processLargeCSV = async () => {
try {
const results = await LargeFileHandler.processCSVFile(
'large-data.csv',
async (line, lineNumber) => {
// 跳过标题行
if (lineNumber === 1) return null;
const columns = line.split(',');
// 处理每行数据
return {
id: columns[0],
name: columns[1],
processed: true
};
}
);
console.log(`Processed ${results.length} records`);
} catch (error) {
console.error('Failed to process CSV:', error.message);
}
};
网络和 API 问题
1. HTTP 请求错误处理
javascript
// HTTP 客户端错误处理
const axios = require('axios');
class HTTPClient {
constructor(baseURL, timeout = 10000) {
this.client = axios.create({
baseURL,
timeout,
headers: {
'User-Agent': 'MyApp/1.0.0'
}
});
this.setupInterceptors();
}
setupInterceptors() {
// 请求拦截器
this.client.interceptors.request.use(
(config) => {
console.log(`Making ${config.method.toUpperCase()} request to ${config.url}`);
return config;
},
(error) => {
console.error('Request setup failed:', error.message);
return Promise.reject(error);
}
);
// 响应拦截器
this.client.interceptors.response.use(
(response) => {
console.log(`Response received: ${response.status} ${response.statusText}`);
return response;
},
(error) => {
return this.handleResponseError(error);
}
);
}
handleResponseError(error) {
if (error.response) {
// 服务器响应了错误状态码
const { status, statusText, data } = error.response;
switch (status) {
case 400:
console.error('Bad Request:', data.message || statusText);
break;
case 401:
console.error('Unauthorized: Please check your credentials');
// 可以触发重新登录
this.handleUnauthorized();
break;
case 403:
console.error('Forbidden: Insufficient permissions');
break;
case 404:
console.error('Not Found: Resource does not exist');
break;
case 429:
console.error('Rate Limited: Too many requests');
// 可以实现退避重试
return this.handleRateLimit(error);
case 500:
console.error('Internal Server Error:', data.message || statusText);
break;
default:
console.error(`HTTP Error ${status}:`, statusText);
}
const customError = new Error(`HTTP ${status}: ${statusText}`);
customError.status = status;
customError.response = error.response;
return Promise.reject(customError);
} else if (error.request) {
// 请求发出但没有收到响应
console.error('Network Error: No response received');
const networkError = new Error('Network connection failed');
networkError.code = 'NETWORK_ERROR';
return Promise.reject(networkError);
} else {
// 请求设置出错
console.error('Request Setup Error:', error.message);
return Promise.reject(error);
}
}
handleUnauthorized() {
// 清除认证信息
localStorage.removeItem('authToken');
// 重定向到登录页面
window.location.href = '/login';
}
async handleRateLimit(error) {
const retryAfter = error.response.headers['retry-after'];
const delay = retryAfter ? parseInt(retryAfter) * 1000 : 5000;
console.log(`Rate limited. Retrying after ${delay}ms`);
await new Promise(resolve => setTimeout(resolve, delay));
// 重试原始请求
return this.client.request(error.config);
}
// 带重试的请求方法
async requestWithRetry(config, maxRetries = 3) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const response = await this.client.request(config);
return response;
} catch (error) {
console.warn(`Attempt ${attempt} failed:`, error.message);
if (attempt === maxRetries) {
throw error;
}
// 指数退避
const delay = Math.pow(2, attempt) * 1000;
console.log(`Retrying in ${delay}ms...`);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
// 并发请求控制
async batchRequests(requests, concurrency = 5) {
const results = [];
for (let i = 0; i < requests.length; i += concurrency) {
const batch = requests.slice(i, i + concurrency);
try {
const batchResults = await Promise.allSettled(
batch.map(request => this.client.request(request))
);
results.push(...batchResults);
} catch (error) {
console.error(`Batch ${Math.floor(i / concurrency) + 1} failed:`, error.message);
}
}
return results;
}
}
// 使用示例
const apiClient = new HTTPClient('https://api.example.com');
const fetchUserData = async (userId) => {
try {
const response = await apiClient.requestWithRetry({
method: 'GET',
url: `/users/${userId}`
});
return response.data;
} catch (error) {
console.error('Failed to fetch user data:', error.message);
throw error;
}
};
2. 数据库连接问题
javascript
// 数据库连接错误处理
const mongoose = require('mongoose');
const mysql = require('mysql2/promise');
class DatabaseManager {
constructor() {
this.mongoConnection = null;
this.mysqlPool = null;
this.reconnectAttempts = 0;
this.maxReconnectAttempts = 5;
}
// MongoDB 连接管理
async connectMongoDB(uri, options = {}) {
const defaultOptions = {
useNewUrlParser: true,
useUnifiedTopology: true,
maxPoolSize: 10,
serverSelectionTimeoutMS: 5000,
socketTimeoutMS: 45000,
bufferMaxEntries: 0,
bufferCommands: false,
...options
};
try {
await mongoose.connect(uri, defaultOptions);
console.log('MongoDB connected successfully');
this.setupMongoEventHandlers();
this.reconnectAttempts = 0;
return mongoose.connection;
} catch (error) {
console.error('MongoDB connection failed:', error.message);
await this.handleMongoReconnect(uri, defaultOptions);
}
}
setupMongoEventHandlers() {
mongoose.connection.on('error', (error) => {
console.error('MongoDB error:', error.message);
});
mongoose.connection.on('disconnected', () => {
console.warn('MongoDB disconnected');
this.handleMongoReconnect();
});
mongoose.connection.on('reconnected', () => {
console.log('MongoDB reconnected');
this.reconnectAttempts = 0;
});
}
async handleMongoReconnect(uri, options) {
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
console.error('Max reconnection attempts reached for MongoDB');
return;
}
this.reconnectAttempts++;
const delay = Math.pow(2, this.reconnectAttempts) * 1000;
console.log(`Attempting MongoDB reconnection ${this.reconnectAttempts}/${this.maxReconnectAttempts} in ${delay}ms`);
setTimeout(async () => {
try {
await mongoose.connect(uri, options);
} catch (error) {
console.error('MongoDB reconnection failed:', error.message);
await this.handleMongoReconnect(uri, options);
}
}, delay);
}
// MySQL 连接池管理
async createMySQLPool(config) {
const defaultConfig = {
host: 'localhost',
port: 3306,
connectionLimit: 10,
acquireTimeout: 60000,
timeout: 60000,
reconnect: true,
...config
};
try {
this.mysqlPool = mysql.createPool(defaultConfig);
// 测试连接
const connection = await this.mysqlPool.getConnection();
await connection.ping();
connection.release();
console.log('MySQL pool created successfully');
this.setupMySQLEventHandlers();
return this.mysqlPool;
} catch (error) {
console.error('MySQL pool creation failed:', error.message);
throw error;
}
}
setupMySQLEventHandlers() {
this.mysqlPool.on('connection', (connection) => {
console.log('New MySQL connection established');
});
this.mysqlPool.on('error', (error) => {
console.error('MySQL pool error:', error.message);
if (error.code === 'PROTOCOL_CONNECTION_LOST') {
console.log('Attempting to reconnect to MySQL...');
// 连接池会自动重连
}
});
}
// 安全的数据库查询
async safeMongoQuery(model, operation, ...args) {
try {
if (mongoose.connection.readyState !== 1) {
throw new Error('MongoDB not connected');
}
const result = await model[operation](...args);
return { success: true, data: result };
} catch (error) {
console.error(`MongoDB ${operation} failed:`, error.message);
return { success: false, error: error.message };
}
}
async safeMySQLQuery(query, params = []) {
let connection;
try {
if (!this.mysqlPool) {
throw new Error('MySQL pool not initialized');
}
connection = await this.mysqlPool.getConnection();
const [rows, fields] = await connection.execute(query, params);
return { success: true, data: rows, fields };
} catch (error) {
console.error('MySQL query failed:', error.message);
return { success: false, error: error.message };
} finally {
if (connection) {
connection.release();
}
}
}
// 健康检查
async checkDatabaseHealth() {
const health = {
mongodb: { connected: false, error: null },
mysql: { connected: false, error: null }
};
// 检查 MongoDB
try {
if (mongoose.connection.readyState === 1) {
await mongoose.connection.db.admin().ping();
health.mongodb.connected = true;
}
} catch (error) {
health.mongodb.error = error.message;
}
// 检查 MySQL
try {
if (this.mysqlPool) {
const connection = await this.mysqlPool.getConnection();
await connection.ping();
connection.release();
health.mysql.connected = true;
}
} catch (error) {
health.mysql.error = error.message;
}
return health;
}
// 优雅关闭
async gracefulShutdown() {
console.log('Shutting down database connections...');
try {
if (mongoose.connection.readyState === 1) {
await mongoose.connection.close();
console.log('MongoDB connection closed');
}
if (this.mysqlPool) {
await this.mysqlPool.end();
console.log('MySQL pool closed');
}
} catch (error) {
console.error('Error during database shutdown:', error.message);
}
}
}
// 使用示例
const dbManager = new DatabaseManager();
const initializeDatabases = async () => {
try {
await dbManager.connectMongoDB(process.env.MONGODB_URI);
await dbManager.createMySQLPool({
host: process.env.MYSQL_HOST,
user: process.env.MYSQL_USER,
password: process.env.MYSQL_PASSWORD,
database: process.env.MYSQL_DATABASE
});
console.log('All databases initialized');
} catch (error) {
console.error('Database initialization failed:', error.message);
process.exit(1);
}
};
// 优雅关闭处理
process.on('SIGINT', async () => {
await dbManager.gracefulShutdown();
process.exit(0);
});
process.on('SIGTERM', async () => {
await dbManager.gracefulShutdown();
process.exit(0);
});
调试技巧
1. 日志和调试
javascript
// 高级日志系统
const winston = require('winston');
const path = require('path');
class Logger {
constructor() {
this.logger = winston.createLogger({
level: process.env.LOG_LEVEL || 'info',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.errors({ stack: true }),
winston.format.json()
),
defaultMeta: { service: 'myapp' },
transports: [
// 错误日志
new winston.transports.File({
filename: path.join(__dirname, '../logs/error.log'),
level: 'error',
maxsize: 5242880, // 5MB
maxFiles: 5
}),
// 组合日志
new winston.transports.File({
filename: path.join(__dirname, '../logs/combined.log'),
maxsize: 5242880,
maxFiles: 5
})
]
});
// 开发环境添加控制台输出
if (process.env.NODE_ENV !== 'production') {
this.logger.add(new winston.transports.Console({
format: winston.format.combine(
winston.format.colorize(),
winston.format.simple()
)
}));
}
}
// 结构化日志方法
info(message, meta = {}) {
this.logger.info(message, { ...meta, timestamp: new Date().toISOString() });
}
error(message, error = null, meta = {}) {
const errorMeta = error ? {
error: {
message: error.message,
stack: error.stack,
code: error.code
}
} : {};
this.logger.error(message, { ...meta, ...errorMeta, timestamp: new Date().toISOString() });
}
warn(message, meta = {}) {
this.logger.warn(message, { ...meta, timestamp: new Date().toISOString() });
}
debug(message, meta = {}) {
this.logger.debug(message, { ...meta, timestamp: new Date().toISOString() });
}
// HTTP 请求日志
logRequest(req, res, duration) {
this.info('HTTP Request', {
method: req.method,
url: req.url,
statusCode: res.statusCode,
duration: `${duration}ms`,
ip: req.ip,
userAgent: req.get('User-Agent'),
userId: req.user?.id
});
}
// 数据库操作日志
logDatabaseOperation(operation, collection, query, duration, error = null) {
const logData = {
operation,
collection,
query: JSON.stringify(query),
duration: `${duration}ms`
};
if (error) {
this.error('Database Operation Failed', error, logData);
} else {
this.info('Database Operation', logData);
}
}
// 性能日志
logPerformance(operation, duration, meta = {}) {
const level = duration > 1000 ? 'warn' : 'info';
this.logger.log(level, 'Performance Metric', {
operation,
duration: `${duration}ms`,
...meta,
timestamp: new Date().toISOString()
});
}
}
const logger = new Logger();
// 调试工具类
class Debugger {
constructor() {
this.enabled = process.env.DEBUG === 'true';
this.timers = new Map();
}
// 条件调试
debug(message, data = null) {
if (this.enabled) {
console.log(`[DEBUG] ${new Date().toISOString()} - ${message}`);
if (data) {
console.log(JSON.stringify(data, null, 2));
}
}
}
// 性能计时
startTimer(label) {
this.timers.set(label, Date.now());
this.debug(`Timer started: ${label}`);
}
endTimer(label) {
const startTime = this.timers.get(label);
if (startTime) {
const duration = Date.now() - startTime;
this.timers.delete(label);
this.debug(`Timer ended: ${label} - ${duration}ms`);
return duration;
}
return null;
}
// 内存使用情况
logMemoryUsage(label = '') {
if (this.enabled) {
const usage = process.memoryUsage();
console.log(`[MEMORY] ${label}:`, {
rss: `${Math.round(usage.rss / 1024 / 1024)}MB`,
heapTotal: `${Math.round(usage.heapTotal / 1024 / 1024)}MB`,
heapUsed: `${Math.round(usage.heapUsed / 1024 / 1024)}MB`,
external: `${Math.round(usage.external / 1024 / 1024)}MB`
});
}
}
// 函数执行跟踪
trace(fn, context = null) {
return function(...args) {
const label = `${fn.name || 'anonymous'}_${Date.now()}`;
debugger.debug(`Function called: ${fn.name}`, { args });
debugger.startTimer(label);
try {
const result = fn.apply(context, args);
// 处理 Promise
if (result && typeof result.then === 'function') {
return result
.then(value => {
debugger.endTimer(label);
debugger.debug(`Function completed: ${fn.name}`, { result: value });
return value;
})
.catch(error => {
debugger.endTimer(label);
debugger.debug(`Function failed: ${fn.name}`, { error: error.message });
throw error;
});
}
debugger.endTimer(label);
debugger.debug(`Function completed: ${fn.name}`, { result });
return result;
} catch (error) {
debugger.endTimer(label);
debugger.debug(`Function failed: ${fn.name}`, { error: error.message });
throw error;
}
};
}
// 对象深度检查
inspect(obj, label = 'Object') {
if (this.enabled) {
console.log(`[INSPECT] ${label}:`);
console.dir(obj, { depth: null, colors: true });
}
}
}
const debugger = new Debugger();
// 中间件:请求日志
const requestLogger = (req, res, next) => {
const startTime = Date.now();
res.on('finish', () => {
const duration = Date.now() - startTime;
logger.logRequest(req, res, duration);
});
next();
};
// 中间件:错误处理
const errorHandler = (error, req, res, next) => {
logger.error('Unhandled Error', error, {
method: req.method,
url: req.url,
ip: req.ip,
userAgent: req.get('User-Agent')
});
// 不在生产环境暴露错误详情
const isDevelopment = process.env.NODE_ENV === 'development';
res.status(error.status || 500).json({
error: {
message: error.message,
...(isDevelopment && { stack: error.stack })
}
});
};
// 使用示例
const tracedFunction = debugger.trace(async function fetchUserData(userId) {
debugger.logMemoryUsage('Before DB query');
const user = await User.findById(userId);
debugger.logMemoryUsage('After DB query');
debugger.inspect(user, 'Fetched User');
return user;
});
module.exports = {
Logger,
logger,
Debugger,
debugger,
requestLogger,
errorHandler
};
2. 性能分析
javascript
// 性能分析工具
class PerformanceAnalyzer {
constructor() {
this.metrics = new Map();
this.hooks = new Map();
}
// 创建性能标记
mark(name) {
if (typeof performance !== 'undefined') {
performance.mark(name);
} else {
// Node.js 环境
this.metrics.set(name, process.hrtime.bigint());
}
}
// 测量性能
measure(name, startMark, endMark) {
if (typeof performance !== 'undefined') {
performance.measure(name, startMark, endMark);
const measure = performance.getEntriesByName(name)[0];
return measure.duration;
} else {
// Node.js 环境
const start = this.metrics.get(startMark);
const end = this.metrics.get(endMark);
if (start && end) {
const duration = Number(end - start) / 1000000; // 转换为毫秒
return duration;
}
return null;
}
}
// 函数性能分析
profile(fn, name = fn.name || 'anonymous') {
return async function(...args) {
const startMark = `${name}-start-${Date.now()}`;
const endMark = `${name}-end-${Date.now()}`;
analyzer.mark(startMark);
try {
const result = await fn.apply(this, args);
analyzer.mark(endMark);
const duration = analyzer.measure(`${name}-duration`, startMark, endMark);
console.log(`[PROFILE] ${name}: ${duration.toFixed(2)}ms`);
return result;
} catch (error) {
analyzer.mark(endMark);
const duration = analyzer.measure(`${name}-duration`, startMark, endMark);
console.log(`[PROFILE] ${name} (failed): ${duration.toFixed(2)}ms`);
throw error;
}
};
}
// CPU 使用率监控
startCPUMonitoring(interval = 5000) {
const startUsage = process.cpuUsage();
this.cpuInterval = setInterval(() => {
const currentUsage = process.cpuUsage(startUsage);
const userCPU = currentUsage.user / 1000000; // 转换为秒
const systemCPU = currentUsage.system / 1000000;
console.log(`[CPU] User: ${userCPU.toFixed(2)}s, System: ${systemCPU.toFixed(2)}s`);
}, interval);
}
stopCPUMonitoring() {
if (this.cpuInterval) {
clearInterval(this.cpuInterval);
this.cpuInterval = null;
}
}
// 事件循环延迟监控
startEventLoopMonitoring(interval = 1000) {
this.eventLoopInterval = setInterval(() => {
const start = process.hrtime.bigint();
setImmediate(() => {
const delay = Number(process.hrtime.bigint() - start) / 1000000;
if (delay > 10) { // 延迟超过 10ms 警告
console.warn(`[EVENT LOOP] High delay detected: ${delay.toFixed(2)}ms`);
}
});
}, interval);
}
stopEventLoopMonitoring() {
if (this.eventLoopInterval) {
clearInterval(this.eventLoopInterval);
this.eventLoopInterval = null;
}
}
// 生成性能报告
generateReport() {
const memUsage = process.memoryUsage();
const cpuUsage = process.cpuUsage();
return {
timestamp: new Date().toISOString(),
memory: {
rss: Math.round(memUsage.rss / 1024 / 1024),
heapTotal: Math.round(memUsage.heapTotal / 1024 / 1024),
heapUsed: Math.round(memUsage.heapUsed / 1024 / 1024),
external: Math.round(memUsage.external / 1024 / 1024)
},
cpu: {
user: cpuUsage.user / 1000000,
system: cpuUsage.system / 1000000
},
uptime: process.uptime(),
version: process.version,
platform: process.platform
};
}
}
const analyzer = new PerformanceAnalyzer();
// 使用示例
const profiledDatabaseQuery = analyzer.profile(async function queryUsers(filters) {
const users = await User.find(filters);
return users;
});
// 启动监控
analyzer.startCPUMonitoring();
analyzer.startEventLoopMonitoring();
// 定期生成报告
setInterval(() => {
const report = analyzer.generateReport();
console.log('[PERFORMANCE REPORT]', JSON.stringify(report, null, 2));
}, 30000);
module.exports = {
PerformanceAnalyzer,
analyzer
};
总结
Node.js 故障排除需要系统性的方法:
- 错误分类:了解不同类型的错误及其特征
- 防御性编程:实施类型检查和安全的数据访问
- 异步处理:正确处理 Promise 和 async/await
- 内存管理:监控和防止内存泄漏
- 网络处理:实施重试机制和错误恢复
- 调试工具:使用日志、性能分析和调试技术
- 监控系统:实时监控应用健康状况