代码层面优化
- 资源竞争
- 使用队列和任务调度:通过队列来管理异步任务,确保同一时间只有特定数量的任务访问共享资源。例如使用
async
库的 queue
方法。
const async = require('async');
const queue = async.queue((task, callback) => {
// 访问共享资源的操作
console.log('Processing task:', task);
setTimeout(() => {
console.log('Task completed:', task);
callback();
}, 1000);
}, 2); // 同一时间处理2个任务
queue.push([1, 2, 3]);
- **使用锁机制**:在Node.js中可以通过第三方库(如 `mutexify`)实现简单的锁机制。
const Mutex = require('mutexify');
const mutex = new Mutex();
async function accessResource() {
const release = await mutex.acquire();
try {
// 访问共享资源的代码
console.log('Accessing shared resource');
} finally {
release();
}
}
- 内存泄漏
- 及时释放引用:确保不再使用的对象被垃圾回收机制回收。例如在事件监听器中,移除不再需要的监听器。
const EventEmitter = require('events');
const emitter = new EventEmitter();
function listener() {
console.log('Event fired');
}
emitter.on('event', listener);
// 不再需要监听时移除监听器
emitter.removeListener('event', listener);
- **使用 `WeakMap` 和 `WeakSet`**:当对象的生命周期仅取决于其是否被其他对象引用时,使用 `WeakMap` 和 `WeakSet`。
const weakMap = new WeakMap();
const obj = {};
weakMap.set(obj, 'data');
// 当obj不再被其他地方引用时,WeakMap中的相关内容也可被回收
系统配置层面优化
- 资源竞争
- 调整系统参数:在Linux系统中,可以调整
ulimit
参数来增加文件描述符等资源的限制。例如通过 ulimit -n 65535
命令增加文件描述符数量,以应对高并发下的文件操作。
- 负载均衡:使用Nginx等负载均衡器将请求均匀分配到多个Node.js实例上,避免单个实例承受过多压力导致资源竞争。配置示例:
http {
upstream nodejs_servers {
server 192.168.1.10:3000;
server 192.168.1.11:3000;
}
server {
listen 80;
location / {
proxy_pass http://nodejs_servers;
}
}
}
- 内存泄漏
- 监控与告警:使用
Node.js
自带的 process.memoryUsage()
方法结合监控工具(如 Prometheus
和 Grafana
)来实时监控内存使用情况。当内存使用达到一定阈值时触发告警,以便及时排查内存泄漏问题。
- 启用堆转储:在Node.js应用启动时使用
--inspect
和 --heap-profiler
标志,通过Chrome DevTools等工具进行堆分析,定位内存泄漏点。例如:node --inspect --heap-profiler app.js
。
实际案例
- 资源竞争案例:在一个实时聊天应用中,多个用户同时发送消息可能导致对数据库写入的资源竞争。通过使用任务队列,将数据库写入任务按顺序排队处理,避免了资源竞争导致的数据不一致问题。
- 内存泄漏案例:在一个基于Node.js的爬虫应用中,由于没有及时移除事件监听器,随着爬取任务的不断增加,内存持续上升。通过在任务结束时移除不必要的事件监听器,成功解决了内存泄漏问题。