正文
1. Node.js高级异步编程
1.1 Promise深入理解
Promise是JavaScript中处理异步操作的标准方式,Node.js广泛使用Promise来管理异步流程。
1.1.1 创建和使用Promise
// 创建一个Promise
function readFilePromise(filePath) {
return new Promise((resolve, reject) => {
const fs = require('fs');
fs.readFile(filePath, 'utf8', (err, data) => {
if (err) {
reject(err); // 失败时调用
} else {
resolve(data); // 成功时调用
}
});
});
}
// 使用Promise
readFilePromise('config.json')
.then(data => {
console.log('文件内容:', data);
return JSON.parse(data); // 返回新的Promise
})
.then(config => {
console.log('解析后的配置:', config);
return config.databaseUrl; // 链式处理
})
.catch(err => {
console.error('处理过程中出错:', err);
})
.finally(() => {
console.log('无论成功或失败都会执行');
});
1.1.2 Promise组合模式
// 并行执行多个Promise
const promises = [
fetch('https://api.example.com/users'),
fetch('https://api.example.com/posts'),
fetch('https://api.example.com/comments')
];
Promise.all(promises)
.then(responses => Promise.all(responses.map(res => res.json())))
.then(data => {
const [users, posts, comments] = data;
console.log('所有数据加载完成');
console.log(`用户数: ${users.length}`);
console.log(`文章数: ${posts.length}`);
console.log(`评论数: ${comments.length}`);
})
.catch(error => {
console.error('至少一个请求失败:', error);
});
// 获取最先完成的Promise
Promise.race([
fetchWithTimeout('https://api1.example.com/data', 2000),
fetchWithTimeout('https://api2.example.com/data', 2000),
fetchWithTimeout('https://api3.example.com/data', 2000)
])
.then(result => {
console.log('最快的API返回结果:', result);
})
.catch(error => {
console.error('所有API请求失败:', error);
});
// 带超时的fetch
function fetchWithTimeout(url, timeout) {
return Promise.race([
fetch(url).then(response => response.json()),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('请求超时')), timeout)
)
]);
}
// Promise.allSettled - 等待所有Promise完成,无论成功或失败
Promise.allSettled([
Promise.resolve(1),
Promise.reject(new Error('失败')),
Promise.resolve(3)
])
.then(results => {
results.forEach((result, index) => {
if (result.status === 'fulfilled') {
console.log(`Promise ${index} 成功:`, result.value);
} else {
console.log(`Promise ${index} 失败:`, result.reason);
}
});
});
1.2 Async/Await高级模式
Async/Await是基于Promise的语法糖,使异步代码更易读和维护。
1.2.1 基本使用
const fs = require('fs').promises;
async function readConfigFile() {
try {
// 等待异步操作完成
const data = await fs.readFile('config.json', 'utf8');
const config = JSON.parse(data);
return config;
} catch (error) {
console.error('读取配置文件失败:', error);
throw error; // 重新抛出错误
}
}
// 调用异步函数
readConfigFile()
.then(config => {
console.log('配置加载成功:', config);
})
.catch(err => {
console.error('配置加载失败:', err);
});
1.2.2 并行执行
const fs = require('fs').promises;
async function loadMultipleFiles() {
try {
// 并行读取多个文件
const [config, users, logs] = await Promise.all([
fs.readFile('config.json', 'utf8'),
fs.readFile('users.json', 'utf8'),
fs.readFile('logs.txt', 'utf8')
]);
return {
config: JSON.parse(config),
users: JSON.parse(users),
logs: logs.split('\n')
};
} catch (error) {
console.error('加载文件失败:', error);
throw error;
}
}
1.2.3 顺序执行与错误处理
async function processUserData(userIds) {
const results = [];
// 顺序处理每个用户
for (const id of userIds) {
try {
const userData = await fetchUserData(id);
const processedData = await processData(userData);
results.push({
id,
data: processedData,
status: 'success'
});
} catch (error) {
console.error(`处理用户 ${id} 失败:`, error);
results.push({
id,
error: error.message,
status: 'error'
});
// 继续处理下一个用户
}
}
return results;
}
// 自定义异步迭代器
async function* asyncGenerator() {
let i = 0;
while (i < 5) {
await new Promise(resolve => setTimeout(resolve, 1000));
yield i++;
}
}
// 使用异步迭代器
async function useAsyncGenerator() {
for await (const num of asyncGenerator()) {
console.log(`生成数字: ${num}`);
}
}
1.3 事件循环高级概念
深入理解Node.js事件循环对于编写高性能应用至关重要。
1.3.1 事件循环阶段详解
const fs = require('fs');
// 展示事件循环各阶段执行顺序
console.log('1. 脚本开始');
// 定时器阶段
setTimeout(() => {
console.log('2. setTimeout 回调');
// 嵌套的定时器
setTimeout(() => {
console.log('6. 嵌套的 setTimeout');
}, 0);
// 立即执行
setImmediate(() => {
console.log('7. 内部 setImmediate');
});
// 添加到下一个事件循环的微任务队列
Promise.resolve().then(() => {
console.log('8. 内部 Promise.then');
});
}, 0);
// 检查阶段
setImmediate(() => {
console.log('4. setImmediate 回调');
});
// I/O回调阶段
fs.readFile(__filename, () => {
console.log('5. I/O 回调');
// 检查阶段
setImmediate(() => {
console.log('9. I/O内部 setImmediate');
});
// 定时器阶段
setTimeout(() => {
console.log('10. I/O内部 setTimeout');
}, 0);
});
// 微任务(在每个阶段之间执行)
Promise.resolve().then(() => {
console.log('3. Promise.then 微任务');
});
console.log('0. 脚本结束');
// 输出顺序可能是:
// 1. 脚本开始
// 0. 脚本结束
// 3. Promise.then 微任务
// 2. setTimeout 回调
// 8. 内部 Promise.then
// 4. setImmediate 回调
// 5. I/O 回调
// 9. I/O内部 setImmediate
// 6. 嵌套的 setTimeout
// 7. 内部 setImmediate
// 10. I/O内部 setTimeout
1.3.2 微任务与宏任务
console.log('1. 开始');
// 宏任务
setTimeout(() => {
console.log('2. setTimeout 宏任务');
// 微任务
Promise.resolve().then(() => {
console.log('3. setTimeout内的微任务');
});
// 微任务
process.nextTick(() => {
console.log('4. setTimeout内的nextTick');
});
}, 0);
// 微任务
Promise.resolve().then(() => {
console.log('5. Promise.then 微任务');
// 微任务
process.nextTick(() => {
console.log('6. Promise内的nextTick');
});
});
// 微任务(优先级高于Promise)
process.nextTick(() => {
console.log('7. process.nextTick 微任务');
});
console.log('8. 结束');
// 输出顺序:
// 1. 开始
// 8. 结束
// 7. process.nextTick 微任务
// 5. Promise.then 微任务
// 6. Promise内的nextTick
// 2. setTimeout 宏任务
// 4. setTimeout内的nextTick
// 3. setTimeout内的微任务
1.3.3 控制事件循环
// 使用setImmediate延迟执行
function delayOperation() {
// 将耗时操作放入检查阶段
setImmediate(() => {
// 执行耗时操作
const result = performHeavyCalculation();
console.log('计算结果:', result);
});
}
// 使用process.nextTick优先执行
function priorityOperation(callback) {
process.nextTick(() => {
const result = performQuickOperation();
callback(result);
});
}
// 使用queueMicrotask添加微任务
function scheduleMicrotask() {
queueMicrotask(() => {
console.log('这是一个微任务');
});
}
2. Node.js流和缓冲区高级应用
2.1 高级流操作
2.1.1 自定义流实现
const { Readable, Writable, Transform, Duplex } = require('stream');
// 自定义可读流
class CounterStream extends Readable {
constructor(max) {
super();
this.max = max;
this.counter = 0;
}
_read() {
this.counter++;
if (this.counter <= this.max) {
// 推送数据到流
const data = `计数: ${this.counter}\n`;
this.push(data);
} else {
// 结束流
this.push(null);
}
}
}
// 自定义可写流
class LoggerStream extends Writable {
constructor(options) {
super(options);
this.count = 0;
}
_write(chunk, encoding, callback) {
this.count++;
console.log(`[写入 #${this.count}]: ${chunk.toString().trim()}`);
// 表示写入完成
callback();
}
}
// 自定义转换流
class UppercaseTransform extends Transform {
_transform(chunk, encoding, callback) {
// 转换数据
const upperChunk = chunk.toString().toUpperCase();
// 推送转换后的数据
this.push(upperChunk);
// 表示转换完成
callback();
}
}
// 使用自定义流
const counter = new CounterStream(5);
const logger = new LoggerStream();
const upperCase = new UppercaseTransform();
counter
.pipe(upperCase) // 转换为大写
.pipe(logger); // 输出结果
2.1.2 流的背压处理
const fs = require('fs');
const { pipeline } = require('stream');
// 创建大文件读取流
const readStream = fs.createReadStream('large-file.txt', {
highWaterMark: 64 * 1024 // 64KB缓冲区
});
// 创建写入流,较小的缓冲区
const writeStream = fs.createWriteStream('output-file.txt', {
highWaterMark: 16 * 1024 // 16KB缓冲区
});
// 监控背压情况
readStream.on('data', (chunk) => {
// 尝试写入数据
const canContinue = writeStream.write(chunk);
if (!canContinue) {
console.log('背压发生,暂停读取');
// 暂停读取流,等待写入流排空
readStream.pause();
}
});
// 当写入流准备接收更多数据时
writeStream.on('drain', () => {
console.log('写入流已排空,继续读取');
// 恢复读取流
readStream.resume();
});
// 使用pipeline API处理错误和清理
pipeline(
fs.createReadStream('source.txt'),
new UppercaseTransform(),
fs.createWriteStream('destination.txt'),
(err) => {
if (err) {
console.error('Pipeline失败', err);
} else {
console.log('Pipeline成功完成');
}
}
);
2.1.3 对象模式流
const { Transform } = require('stream');
// 创建对象模式转换流
class ObjectTransformer extends Transform {
constructor(options) {
// 启用对象模式
super({ objectMode: true, ...options });
}
_transform(chunk, encoding, callback) {
// 处理对象数据
if (typeof chunk === 'object') {
// 转换对象
const transformedObject = {
...chunk,
timestamp: Date.now(),
transformed: true
};
// 推送转换后的对象
this.push(transformedObject);
}
callback();
}
}
// 使用对象模式流
const objectStream = new ObjectTransformer();
objectStream.on('data', (data) => {
console.log('转换后的对象:', data);
});
// 写入对象到流
objectStream.write({ name: 'John', age: 30 });
objectStream.write({ name: 'Alice', age: 25 });
objectStream.end();
2.2 Buffer高级操作
Buffer是Node.js中处理二进制数据的核心类。
2.2.1 Buffer创建和操作
// 创建Buffer的不同方式
const buffer1 = Buffer.alloc(10); // 创建10字节的空Buffer
const buffer2 = Buffer.from('Hello, Node.js', 'utf8'); // 从字符串创建
const buffer3 = Buffer.from([0x48, 0x65, 0x6c, 0x6c, 0x6f]); // 从字节数组创建
// Buffer操作
console.log(buffer2.toString()); // 转换为字符串
console.log(buffer2.toString('hex')); // 转换为十六进制字符串
console.log(buffer2.length); // Buffer长度
// 写入Buffer
buffer1.write('Hi', 0, 2);
console.log(buffer1.toString()); // 'Hi' + 8个空字节
// 复制Buffer
const bufferSource = Buffer.from('ABC');
const bufferTarget = Buffer.alloc(3);
bufferSource.copy(bufferTarget);
console.log(bufferTarget.toString()); // 'ABC'
// 切片Buffer (共享内存)
const originalBuffer = Buffer.from('Hello, World!');
const slicedBuffer = originalBuffer.slice(0, 5);
slicedBuffer[0] = 0x4A; // 修改为'J'
console.log(slicedBuffer.toString()); // 'Jello'
console.log(originalBuffer.toString()); // 'Jello, World!'
2.2.2 Buffer池和性能优化
// 使用Buffer池
const pooledBuffers = [];
function createPooledBuffer(size) {
// 对于小Buffer使用Buffer.allocUnsafe(),它可能使用池
if (size < 4096) {
return Buffer.allocUnsafe(size);
} else {
// 大Buffer直接分配
return Buffer.alloc(size);
}
}
// 创建多个小Buffer
for (let i = 0; i < 1000; i++) {
const buf = createPooledBuffer(100);
buf.fill(0); // 确保没有敏感数据
pooledBuffers.push(buf);
}
// 内存使用分析
const memoryUsage = process.memoryUsage();
console.log(`堆总大小: ${memoryUsage.heapTotal / 1024 / 1024} MB`);
console.log(`堆已使用: ${memoryUsage.heapUsed / 1024 / 1024} MB`);
console.log(`外部内存: ${memoryUsage.external / 1024 / 1024} MB`);
2.2.3 二进制数据处理
// 处理二进制协议
function parseBinaryProtocol(buffer) {
let offset = 0;
// 读取头部(4字节)
const header = buffer.readUInt32BE(offset);
offset += 4;
// 读取消息类型(1字节)
const messageType = buffer.readUInt8(offset);
offset += 1;
// 读取数据长度(2字节)
const dataLength = buffer.readUInt16BE(offset);
offset += 2;
// 读取数据
const data = buffer.slice(offset, offset + dataLength);
offset += dataLength;
// 读取校验和(4字节)
const checksum = buffer.readUInt32BE(offset);
return {
header,
messageType,
dataLength,
data,
checksum
};
}
// 创建二进制消息
function createBinaryMessage(messageType, data) {
const dataBuffer = Buffer.from(data);
const dataLength = dataBuffer.length;
// 计算总长度: 头部(4) + 消息类型(1) + 数据长度(2) + 数据 + 校验和(4)
const totalLength = 4 + 1 + 2 + dataLength + 4;
// 创建消息buffer
const message = Buffer.alloc(totalLength);
let offset = 0;
// 写入头部(固定值)
message.writeUInt32BE(0x01020304, offset);
offset += 4;
// 写入消息类型
message.writeUInt8(messageType, offset);
offset += 1;
// 写入数据长度
message.writeUInt16BE(dataLength, offset);
offset += 2;
// 写入数据
dataBuffer.copy(message, offset);
offset += dataLength;
// 简单校验和(实际应用中应使用更复杂的算法)
const checksum = calculateChecksum(message, 0, offset);
message.writeUInt32BE(checksum, offset);
return message;
}
// 简单校验和计算
function calculateChecksum(buffer, start, end) {
let sum = 0;
for (let i = start; i < end; i++) {
sum += buffer[i];
}
return sum & 0xFFFFFFFF; // 保持为32位
}
3. Node.js性能优化与调试
3.1 性能分析工具
3.1.1 内置性能分析
// 使用console.time测量执行时间
console.time('操作');
// 执行耗时操作
for (let i = 0; i < 1000000; i++) {
// 一些计算
}
console.timeEnd('操作'); // 输出: 操作: 10.123ms
// 使用process.hrtime进行高精度计时
const start = process.hrtime();
// 执行操作
const diff = process.hrtime(start);
console.log(`执行时间: ${diff[0] * 1e9 + diff[1]} 纳秒`);
// 使用process.memoryUsage监控内存
function monitorMemory() {
const memUsage = process.memoryUsage();
console.log('内存使用:');
console.log(` RSS: ${Math.round(memUsage.rss / 1024 / 1024)} MB`);
console.log(` 堆总大小: ${Math.round(memUsage.heapTotal / 1024 / 1024)} MB`);
console.log(` 堆已用: ${Math.round(memUsage.heapUsed / 1024 / 1024)} MB`);
console.log(` 外部: ${Math.round(memUsage.external / 1024 / 1024)} MB`);
}
// 定期检查内存使用
const memoryInterval = setInterval(monitorMemory, 5000);
3.1.2 使用Node.js内置性能钩子
const { PerformanceObserver, performance } = require('perf_hooks');
// 创建性能观察器
const obs = new PerformanceObserver((items) => {
const entries = items.getEntries();
entries.forEach((entry) => {
console.log(`${entry.name}: ${entry.duration}ms`);
});
});
// 订阅所有性能事件
obs.observe({ entryTypes: ['measure', 'function'] });
// 标记开始
performance.mark('操作-开始');
// 执行一些操作
someFunction();
// 标记结束
performance.mark('操作-结束');
// 测量两个标记之间的持续时间
performance.measure('操作持续时间', '操作-开始', '操作-结束');
// 使用timerify包装函数进行性能测量
const timerify = performance.timerify;
function someFunction() {
// 耗时操作
const arr = new Array(1000000);
for (let i = 0; i < 1000000; i++) {
arr[i] = Math.random();
}
arr.sort();
}
// 包装函数
const timerifiedFunction = timerify(someFunction);
// 调用函数并测量性能
timerifiedFunction();
3.1.3 使用Clinic.js进行性能诊断
# 安装Clinic.js
npm install -g clinic
# 使用Doctor分析整体性能
clinic doctor -- node app.js
# 使用Bubbleprof分析异步操作
clinic bubbleprof -- node app.js
# 使用Flame生成火焰图
clinic flame -- node app.js
3.2 内存泄漏检测与修复
3.2.1 常见内存泄漏场景
// 1. 闭包导致的内存泄漏
function createLeak() {
const largeData = new Array(1000000).fill('x');
return function leakingFunction() {
// 引用了外部的largeData,导致无法垃圾回收
console.log(largeData.length);
};
}
// 修复方法: 不保留对大数据的引用
function createFixed() {
const largeData = new Array(1000000).fill('x');
const length = largeData.length;
return function fixedFunction() {
// 只保留需要的数据
console.log(length);
};
}
// 2. 事件监听器未移除
function setupEventHandlers() {
const element = {
addEventListener: (event, handler) => {
this.handler = handler;
}
};
element.addEventListener('data', function dataHandler() {
console.log('Data received');
});
// 未移除事件处理器,可能导致泄漏
return element;
}
// 修复方法: 保存引用并移除
function setupFixedEventHandlers() {
const element = {
addEventListener: (event, handler) => {
this.handler = handler;
},
removeEventListener: () => {
this.handler = null;
}
};
const handler = function dataHandler() {
console.log('Data received');
};
element.addEventListener('data', handler);
// 提供移除方法
return {
element,
cleanup: () => element.removeEventListener('data', handler)
};
}
// 3. 缓存未限制大小
const cache = {};
function addToCache(key, value) {
// 无限制添加到缓存,可能导致内存泄漏
cache[key] = value;
}
// 修复方法: 限制缓存大小
const LRUCache = require('lru-cache');
const limitedCache = new LRUCache({
max: 500, // 最多存储项目数
maxAge: 1000 * 60 * 60 // 项目最长存活时间(1小时)
});
function addToLimitedCache(key, value) {
limitedCache.set(key, value);
}
3.2.2 使用堆快照分析内存
// 生成堆快照
const heapdump = require('heapdump');
// 生成堆快照文件
function generateHeapSnapshot() {
const filename = `${Date.now()}.heapsnapshot`;
heapdump.writeSnapshot(filename, (err) => {
if (err) console.error('堆快照生成失败:', err);
else console.log(`堆快照已保存到 ${filename}`);
});
}
// 在特定条件下生成堆快照
let memoryThreshold = 500; // MB
function checkMemoryUsage() {
const memUsage = process.memoryUsage();
const usedMemoryMB = memUsage.heapUsed / 1024 / 1024;
if (usedMemoryMB > memoryThreshold) {
console.log(`内存使用超过阈值: ${usedMemoryMB.toFixed(2)} MB`);
generateHeapSnapshot();
memoryThreshold += 100; // 提高阈值,避免生成太多快照
}
}
// 定期检查内存使用
setInterval(checkMemoryUsage, 30000);
3.3 CPU性能优化
3.3.1 避免阻塞主线程
// 不良实践: 阻塞主线程
function blockingOperation() {
// 耗时计算
let sum = 0;
for (let i = 0; i < 1e9; i++) {
sum += i;
}
return sum;
}
// 良好实践: 使用setImmediate拆分任务
function nonBlockingOperation(callback) {
let sum = 0;
let i = 0;
const iterations = 1e9;
const chunk = 1e6;
function processChunk() {
// 处理一小块数据
const end = Math.min(i + chunk, iterations);
for (; i < end; i++) {
sum += i;
}
// 检查是否完成
if (i < iterations) {
setImmediate(processChunk);
} else {
callback(sum);
}
}
// 开始处理
processChunk();
}
// 使用Worker Threads处理CPU密集型任务
const { Worker, isMainThread, parentPort, workerData } = require('worker_threads');
// 使用Worker Threads处理CPU密集型任务
const { Worker, isMainThread, parentPort, workerData } = require('worker_threads');
if (isMainThread) {
// 主线程代码
function runWorker(data) {
return new Promise((resolve, reject) => {
// 创建新的工作线程
const worker = new Worker(__filename, {
workerData: data
});
// 接收工作线程的消息
worker.on('message', resolve);
worker.on('error', reject);
worker.on('exit', (code) => {
if (code !== 0) {
reject(new Error(`Worker stopped with exit code ${code}`));
}
});
});
}
// 使用工作线程处理任务
async function main() {
try {
const result = await runWorker({ numbers: Array.from({length: 10000000}, (_, i) => i) });
console.log('计算结果:', result);
} catch (err) {
console.error('工作线程错误:', err);
}
}
main();
} else {
// 工作线程代码
const { numbers } = workerData;
// 执行CPU密集型计算
const sum = numbers.reduce((acc, val) => acc + val, 0);
// 将结果发送回主线程
parentPort.postMessage(sum);
}
3.3.2 代码优化技巧
// 1. 避免频繁创建对象
// 不良实践
function createObjects() {
const results = [];
for (let i = 0; i < 1000000; i++) {
// 每次迭代创建新对象
results.push({ index: i, value: i * 2 });
}
return results;
}
// 良好实践: 对象复用
function reuseObjects() {
const results = [];
const obj = {};
for (let i = 0; i < 1000000; i++) {
// 重用同一个对象
obj.index = i;
obj.value = i * 2;
// 存储对象的拷贝
results.push({...obj});
}
return results;
}
// 2. 使用适当的数据结构
// 不良实践: 数组查找
function findInArray(id) {
const items = Array.from({length: 10000}, (_, i) => ({ id: `id-${i}`, value: i }));
// O(n)复杂度的查找
return items.find(item => item.id === id);
}
// 良好实践: 使用Map
function findInMap(id) {
const items = new Map();
// 填充Map
for (let i = 0; i < 10000; i++) {
items.set(`id-${i}`, { id: `id-${i}`, value: i });
}
// O(1)复杂度的查找
return items.get(id);
}
// 3. 避免深层嵌套循环
// 不良实践: 嵌套循环
function nestedLoops(matrix) {
let sum = 0;
for (let i = 0; i < matrix.length; i++) {
for (let j = 0; j < matrix[i].length; j++) {
for (let k = 0; k < matrix[i][j].length; k++) {
sum += matrix[i][j][k];
}
}
}
return sum;
}
// 良好实践: 使用flat和reduce
function flattenAndReduce(matrix) {
// 扁平化多维数组并求和
return matrix.flat(2).reduce((sum, val) => sum + val, 0);
}
3.3.3 使用V8优化提示
// 1. 使用类型一致的数组
// 不良实践: 混合类型数组
const mixedArray = [1, 'string', true, {}, 3.14];
// 良好实践: 类型一致的数组
const numbersArray = new Float64Array(1000);
for (let i = 0; i < 1000; i++) {
numbersArray[i] = i * 1.1;
}
// 2. 避免动态修改对象结构
// 不良实践: 动态添加属性
function createDynamicObject() {
const obj = {};
obj.name = 'John';
obj.age = 30;
// 后续添加属性会导致隐藏类变化
obj.address = 'Some Street';
return obj;
}
// 良好实践: 一次性创建完整对象
function createCompleteObject() {
// 一次性定义所有属性
return {
name: 'John',
age: 30,
address: 'Some Street'
};
}
// 3. 使用函数内联
// V8可以自动内联小函数,保持函数简洁
function add(a, b) {
return a + b;
}
function calculate() {
let sum = 0;
for (let i = 0; i < 1000; i++) {
// V8可能会内联这个函数调用
sum = add(sum, i);
}
return sum;
}
3.4 调试技巧与工具
3.4.1 使用内置调试器
// 启动调试器: node --inspect app.js
// 或在代码中添加调试器断点
function debugMe() {
let a = 1;
let b = 2;
debugger; // 代码会在这里暂停执行
let c = a + b;
return c;
}
debugMe();
3.4.2 高级日志技巧
// 创建结构化日志
const util = require('util');
// 自定义日志格式化
function structuredLog(level, message, context = {}) {
const timestamp = new Date().toISOString();
const logEntry = {
timestamp,
level,
message,
...context
};
// 格式化输出
console.log(JSON.stringify(logEntry));
}
// 使用不同日志级别
function logger() {
return {
debug: (message, context) => structuredLog('DEBUG', message, context),
info: (message, context) => structuredLog('INFO', message, context),
warn: (message, context) => structuredLog('WARN', message, context),
error: (message, context) => structuredLog('ERROR', message, context)
};
}
const log = logger();
log.info('服务器启动', { port: 3000 });
log.debug('数据库查询', { query: 'SELECT * FROM users', duration: 15 });
log.error('请求失败', { url: '/api/users', statusCode: 500, error: 'Database connection failed' });
// 使用util.inspect进行深度对象检查
function inspectObject(obj) {
return util.inspect(obj, {
showHidden: true,
depth: null,
colors: true
});
}
const complexObject = {
user: {
name: 'John',
profile: {
address: {
street: '123 Main St',
city: 'Anytown'
}
}
},
permissions: new Set(['read', 'write']),
token: Buffer.from('secret-token')
};
console.log(inspectObject(complexObject));
3.4.3 使用APM工具
// 使用Elastic APM监控应用
const apm = require('elastic-apm-node').start({
serviceName: 'my-nodejs-app',
serverUrl: 'http://localhost:8200'
});
// 跟踪自定义事务
function handleRequest(req, res) {
// 开始一个事务
const transaction = apm.startTransaction('GET /api/users', 'request');
try {
// 开始一个跨度(span)
const span = transaction.startSpan('database query');
// 执行数据库查询
const users = fetchUsersFromDatabase();
if (span) span.end();
// 发送响应
res.send(users);
} catch (error) {
// 捕获并报告错误
apm.captureError(error);
res.status(500).send('Internal Server Error');
} finally {
// 结束事务
if (transaction) transaction.end();
}
}
4. Node.js安全最佳实践
4.1 常见安全威胁与防护
4.1.1 输入验证与消毒
const validator = require('validator');
const sanitizeHtml = require('sanitize-html');
// 验证用户输入
function validateUserInput(input) {
const validationResults = {
isValid: true,
errors: []
};
// 检查必填字段
if (!input.email) {
validationResults.isValid = false;
validationResults.errors.push('Email is required');
} else if (!validator.isEmail(input.email)) {
validationResults.isValid = false;
validationResults.errors.push('Invalid email format');
}
// 验证密码强度
if (!input.password) {
validationResults.isValid = false;
validationResults.errors.push('Password is required');
} else if (!validator.isStrongPassword(input.password, {
minLength: 8,
minLowercase: 1,
minUppercase: 1,
minNumbers: 1,
minSymbols: 1
})) {
validationResults.isValid = false;
validationResults.errors.push('Password is not strong enough');
}
// 验证URL
if (input.website && !validator.isURL(input.website)) {
validationResults.isValid = false;
validationResults.errors.push('Invalid website URL');
}
return validationResults;
}
// 清理HTML内容
function sanitizeUserContent(html) {
return sanitizeHtml(html, {
allowedTags: ['b', 'i', 'em', 'strong', 'a', 'p', 'br'],
allowedAttributes: {
'a': ['href', 'target', 'rel']
},
// 强制所有链接在新窗口打开并添加noopener
transformTags: {
'a': (tagName, attribs) => {
return {
tagName,
attribs: {
...attribs,
target: '_blank',
rel: 'noopener noreferrer'
}
};
}
}
});
}
// 防止SQL注入
function buildSafeQuery(knex, table, filters) {
let query = knex(table);
// 安全地添加WHERE条件
if (filters.id) {
query = query.where('id', filters.id);
}
if (filters.status) {
query = query.where('status', filters.status);
}
// 安全地处理LIKE查询
if (filters.search) {
query = query.where('name', 'like', `%${filters.search}%`);
}
return query;
}
4.1.2 防止跨站脚本(XSS)攻击
const express = require('express');
const helmet = require('helmet');
const xss = require('xss');
const app = express();
// 使用Helmet设置安全相关的HTTP头
app.use(helmet());
// 设置CSP (内容安全策略)
app.use(helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "'unsafe-inline'", 'trusted-cdn.com'],
styleSrc: ["'self'", "'unsafe-inline'", 'trusted-cdn.com'],
imgSrc: ["'self'", 'data:', 'trusted-cdn.com'],
connectSrc: ["'self'", 'api.example.com'],
fontSrc: ["'self'", 'trusted-cdn.com'],
objectSrc: ["'none'"],
mediaSrc: ["'self'"],
frameSrc: ["'none'"]
}
}));
// XSS防护中间件
function xssProtection(req, res, next) {
// 清理请求体
if (req.body) {
Object.keys(req.body).forEach(key => {
if (typeof req.body[key] === 'string') {
req.body[key] = xss(req.body[key]);
}
});
}
// 清理查询参数
if (req.query) {
Object.keys(req.query).forEach(key => {
if (typeof req.query[key] === 'string') {
req.query[key] = xss(req.query[key]);
}
});
}
next();
}
app.use(xssProtection);
// 安全地渲染用户提供的内容
app.get('/profile', (req, res) => {
const userBio = getUserBio(req.user.id);
// 在发送到客户端前清理内容
const sanitizedBio = xss(userBio);
res.render('profile', {
user: req.user,
bio: sanitizedBio
});
});
4.1.3 防止跨站请求伪造(CSRF)
const express = require('express');
const csrf = require('csurf');
const cookieParser = require('cookie-parser');
const app = express();
// 解析Cookie
app.use(cookieParser());
// 解析表单数据
app.use(express.urlencoded({ extended: false }));
// 设置CSRF保护
const csrfProtection = csrf({ cookie: true });
// 应用CSRF保护到所有需要的路由
app.get('/form', csrfProtection, (req, res) => {
// 传递CSRF令牌到视图
res.render('form', { csrfToken: req.csrfToken() });
});
app.post('/process', csrfProtection, (req, res) => {
// CSRF验证已通过(中间件会自动验证)
res.send('表单处理成功!');
});
// 处理CSRF错误
app.use((err, req, res, next) => {
if (err.code === 'EBADCSRFTOKEN') {
// 处理CSRF令牌错误
return res.status(403).send('表单已过期或无效。请重新提交。');
}
next(err);
});
// 在前端视图中使用CSRF令牌
/*
<form action="/process" method="post">
<input type="hidden" name="_csrf" value="<%= csrfToken %>">
<input type="text" name="username">
<button type="submit">提交</button>
</form>
*/
4.2 认证与授权
4.2.1 安全的密码处理
const crypto = require('crypto');
const bcrypt = require('bcrypt');
const argon2 = require('argon2');
// 使用bcrypt哈希密码
async function hashPasswordBcrypt(password) {
// 生成盐值(推荐值为10+)
const saltRounds = 12;
try {
// 生成哈希
const hash = await bcrypt.hash(password, saltRounds);
return hash;
} catch (error) {
console.error('密码哈希失败:', error);
throw error;
}
}
// 使用bcrypt验证密码
async function verifyPasswordBcrypt(password, hash) {
try {
// 比较密码和哈希
const match = await bcrypt.compare(password, hash);
return match;
} catch (error) {
console.error('密码验证失败:', error);
return false;
}
}
// 使用Argon2哈希密码(更安全但更消耗资源)
async function hashPasswordArgon2(password) {
try {
// 使用Argon2id变体(推荐)
const hash = await argon2.hash(password, {
type: argon2.argon2id,
memoryCost: 2**16, // 64MB
timeCost: 3, // 3次迭代
parallelism: 2 // 2个并行线程
});
return hash;
} catch (error) {
console.error('Argon2密码哈希失败:', error);
throw error;
}
}
// 使用Argon2验证密码
async function verifyPasswordArgon2(password, hash) {
try {
return await argon2.verify(hash, password);
} catch (error) {
console.error('Argon2密码验证失败:', error);
return false;
}
}
// 生成安全的随机令牌
function generateSecureToken(byteLength = 32) {
return new Promise((resolve, reject) => {
crypto.randomBytes(byteLength, (err, buffer) => {
if (err) {
reject(err);
} else {
resolve(buffer.toString('hex'));
}
});
});
}
4.2.2 JWT认证实现
const jwt = require('jsonwebtoken');
const crypto = require('crypto');
// 生成安全的JWT密钥
const JWT_SECRET = crypto.randomBytes(64).toString('hex');
// 在生产环境中应从环境变量或配置服务获取
// 创建JWT令牌
function generateToken(user) {
// 设置令牌有效期(例如24小时)
const expiresIn = '24h';
// 创建有效载荷(不要包含敏感信息)
const payload = {
sub: user.id, // 主题(用户ID)
name: user.name, // 用户名
role: user.role, // 用户角色
iat: Date.now() // 签发时间
};
// 签署令牌
return jwt.sign(payload, JWT_SECRET, { expiresIn });
}
// 验证JWT令牌
function verifyToken(token) {
try {
// 验证并解码令牌
const decoded = jwt.verify(token, JWT_SECRET);
return { valid: true, expired: false, payload: decoded };
} catch (error) {
// 处理不同类型的错误
return {
valid: false,
expired: error.name === 'TokenExpiredError',
payload: null
};
}
}
// JWT认证中间件
function authenticateJWT(req, res, next) {
// 从请求头获取令牌
const authHeader = req.headers.authorization;
if (authHeader) {
// 提取令牌(格式: "Bearer TOKEN")
const token = authHeader.split(' ')[1];
// 验证令牌
const result = verifyToken(token);
if (result.valid) {
// 将用户信息附加到请求对象
req.user = result.payload;
next();
} else if (result.expired) {
res.status(401).json({ error: 'Token expired' });
} else {
res.status(403).json({ error: 'Invalid token' });
}
} else {
res.status(401).json({ error: 'Authorization header required' });
}
}
// 基于角色的授权中间件
function authorizeRole(requiredRole) {
return (req, res, next) => {
// 检查用户是否已通过认证
if (!req.user) {
return res.status(401).json({ error: 'Authentication required' });
}
// 检查用户角色
if (req.user.role !== requiredRole) {
return res.status(403).json({
error: 'Access denied',
required: requiredRole,
current: req.user.role
});
}
// 用户有所需角色
next();
};
}
// 使用示例
/*
app.post('/login', async (req, res) => {
// 验证用户凭据
const user = await authenticateUser(req.body.username, req.body.password);
if (user) {
// 生成令牌
const token = generateToken(user);
res.json({ token });
} else {
res.status(401).json({ error: 'Invalid credentials' });
}
});
// 受保护的路由
app.get('/api/profile', authenticateJWT, (req, res) => {
res.json({ user: req.user });
});
// 仅管理员可访问的路由
app.get('/api/admin', authenticateJWT, authorizeRole('admin'), (req, res) => {
res.json({ message: 'Admin dashboard' });
});
*/
4.2.3 OAuth 2.0集成
const express = require('express');
const passport = require('passport');
const GoogleStrategy = require('passport-google-oauth20').Strategy;
const session = require('express-session');
const app = express();
// 配置会话
app.use(session({
secret: 'your-secret-key',
resave: false,
saveUninitialized: false,
cookie: {
secure: process.env.NODE_ENV === 'production', // 在生产环境中使用HTTPS
httpOnly: true,
maxAge: 24 * 60 * 60 * 1000 // 24小时
}
}));
// 初始化Passport
app.use(passport.initialize());
app.use(passport.session());
// 配置Google OAuth策略
passport.use(new GoogleStrategy({
clientID: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
callbackURL: "http://localhost:3000/auth/google/callback",
scope: ['profile', 'email']
},
async function(accessToken, refreshToken, profile, done) {
try {
// 查找或创建用户
let user = await findUserByGoogleId(profile.id);
if (!user) {
// 创建新用户
user = await createUser({
googleId: profile.id,
email: profile.emails[0].value,
name: profile.displayName,
picture: profile.photos[0].value
});
}
// 保存令牌(可选)
user.accessToken = accessToken;
user.refreshToken = refreshToken;
await updateUser(user);
return done(null, user);
} catch (error) {
return done(error);
}
}
));
// 序列化和反序列化用户
passport.serializeUser((user, done) => {
done(null, user.id);
});
passport.deserializeUser(async (id, done) => {
try {
const user = await findUserById(id);
done(null, user);
} catch (error) {
done(error);
}
});
// 登录路由
app.get('/auth/google',
passport.authenticate('google', { scope: ['profile', 'email'] })
);
// 回调路由
app.get('/auth/google/callback',
passport.authenticate('google', {
failureRedirect: '/login',
successRedirect: '/dashboard'
})
);
// 登出路由
app.get('/logout', (req, res) => {
req.logout();
res.redirect('/');
});
// 检查认证状态的中间件
function ensureAuthenticated(req, res, next) {
if (req.isAuthenticated()) {
return next();
}
res.redirect('/login');
}
// 受保护的路由
app.get('/dashboard', ensureAuthenticated, (req, res) => {
res.render('dashboard', { user: req.user });
});
// 模拟数据库函数
async function findUserByGoogleId(googleId) {
// 实际应用中应查询数据库
return null; // 假设用户不存在
}
async function createUser(userData) {
// 实际应用中应创建数据库记录
return { id: 'user-123', ...userData };
}
async function updateUser(user) {
// 实际应用中应更新数据库记录
return user;
}
async function findUserById(id) {
// 实际应用中应查询数据库
return { id, name: 'Test User', email: 'test@example.com' };
}
结语
感谢您的阅读!期待您的一键三连!欢迎指正!