nodejs进阶篇:性能优化的多个方法
一、前言
Node.js 以其非阻塞 I/O 和事件驱动的架构在构建高性能网络应用方面表现出色,但仍有许多方面可以进行优化以进一步提升性能。本教程将从多个角度介绍 Node.js 性能优化的方法。
二、代码优化
(一)异步操作优化
- 正确使用异步函数
Node.js 中的异步操作应该尽可能地使用原生的异步方法。例如,在文件系统操作中,使用fs.promises
模块的异步函数,像fs.promises.readFile()
,而不是同步版本。
// 错误示例:同步读取文件,会阻塞事件循环
const fs = require('fs');
const data = fs.readFileSync('file.txt');
// 正确示例:异步读取文件
const fs = require('fs').promises;
fs.readFile('file.txt', 'utf8')
.then((data) => {
console.log(data);
})
.catch((err) => {
console.error(err);
});
- 控制异步操作并发量
当有大量异步操作时,同时发起所有操作可能会耗尽系统资源。可以使用一些工具来限制并发量。例如,使用p - limit
库。
const pLimit = require('p - limit');
const limit = pLimit(5); // 同时最多执行5个异步操作
const urls = ['url1', 'url2',...];
const requests = urls.map((url) => limit(() => fetch(url)));
Promise.all(requests)
.then((responses) => {
// 处理响应
})
.catch((err) => {
console.error(err);
});
(二)内存管理优化
- 避免内存泄漏
- 注意全局变量的使用,尽量减少不必要的全局变量。如果一个对象在全局范围内一直存在且不断增长,可能会导致内存泄漏。
- 对于事件监听器,在不需要时及时移除。例如:
const EventEmitter = require('events');
const emitter = new EventEmitter();
function handler() {
console.log('Event fired');
}
emitter.on('event', handler);
// 假设在某个条件下不再需要监听这个事件
if (someCondition) {
emitter.off('event', handler);
}
- 优化数据结构使用
- 根据需求选择合适的数据结构。如果需要快速查找元素是否存在于集合中,使用
Set
比使用数组遍历查找更高效。
- 根据需求选择合适的数据结构。如果需要快速查找元素是否存在于集合中,使用
const set = new Set([1, 2, 3]);
console.log(set.has(2)); // 比在数组中查找更快
(三)函数优化
- 减少函数嵌套深度
避免过深的函数嵌套,因为这会增加栈帧的开销。可以将复杂的逻辑拆分成多个函数。
// 不好的示例:过多嵌套
function complexFunction() {
if (condition1) {
if (condition2) {
if (condition3) {
// 执行操作
}
}
}
}
// 改进示例
function step1() {
if (condition1) {
return step2();
}
}
function step2() {
if (condition2) {
return step3();
}
}
function step3() {
if (condition3) {
// 执行操作
}
}
- 缓存频繁调用函数的结果对于计算成本高且输入参数相同情况下结果不变的函数,可以使用缓存。
const cache = {};
function expensiveFunction(arg) {
if (cache[arg]) {
return cache[arg];
}
const result = // 复杂计算;
cache[arg] = result;
return result;
}
三、服务器优化
(一)使用集群模式
- 利用多核 CPU
Node.js 的集群模块可以创建多个工作进程来充分利用多核 CPU。以下是一个简单的集群示例:
const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;
if (cluster.isMaster) {
for (let i = 0; i < numCPUs; i++) {
cluster.fork();
}
cluster.on('exit', (worker, code, signal) => {
console.log(`worker ${worker.process.pid} died`);
cluster.fork();
});
} else {
http.createServer((req, res) => {
res.writeHead(200);
res.end('Hello World!');
}).listen(8000);
}
(二)优化 HTTP 服务器配置
- 设置
keep - alive
在创建 HTTP 服务器时,可以设置keep - alive
选项来保持连接活性,减少 TCP 连接建立和拆除的开销。
const http = require('http');
const server = http.createServer((req, res) => {
res.writeHead(200, {
'Connection': 'keep - alive',
'Keep - Alive': 'timeout=5, max=100'
});
res.end('Hello');
});
server.listen(3000);
- 启用 HTTP/2(如果支持)
如果服务器环境和客户端都支持 HTTP/2,可以启用它来提升性能。使用http2
模块(需要相应的配置和支持)。
const http2 = require('http2');
const server = http2.createSecureServer({
key: fs.readFileSync('server - key.pem'),
cert: fs.readFileSync('server - cert.pem')
});
server.on('error', (err) => console.error(err));
server.on('stream', (stream, headers) => {
stream.respond({
'content - type': 'text/plain',
':status': 200
});
stream.end('Hello World');
});
server.listen(8443);
四、数据库和缓存优化
(一)使用数据库连接池
- 配置连接池
对于使用数据库的 Node.js 应用,如使用mysql
数据库,可以使用连接池。
const mysql = require('mysql2');
const pool = mysql.createPool({
host: 'localhost',
user: 'user',
password: 'password',
database: 'database',
waitForConnections: true,
connectionLimit: 10,
queueLimit: 0
});
pool.getConnection((err, connection) => {
if (err) {
console.error(err);
return;
}
connection.query('SELECT * FROM users', (err, rows) => {
connection.release();
if (err) {
console.error(err);
return;
}
console.log(rows);
});
});
(二)应用缓存策略
- 内存缓存使用(以
node - cache
为例)对于一些频繁查询但不经常更新的数据,可以使用内存缓存。
const NodeCache = require('node - cache');
const myCache = new NodeCache();
async function getData() {
const cachedData = myCache.get('key');
if (cachedData) {
return cachedData;
}
const data = await // 从数据库或其他数据源获取数据;
myCache.set('key', data);
return data;
}
来源: Mike张 Web全栈开发者驿站
THE END