实现思路
- 分块读取:为避免一次性读取大量文件导致内存溢出,采用分块读取文件的方式。每次读取文件的一部分,处理完后再读取下一部分。
- 利用事件循环:Node.js基于事件驱动和非阻塞I/O,这使得在读取文件和处理数据时可以高效利用事件循环。在文件读取操作发起后,Node.js不会等待操作完成,而是继续执行其他任务,当文件读取完成后,通过回调函数处理数据。
- 集群(Cluster):考虑到多核CPU的利用,使用Node.js的Cluster模块开启多个工作进程,每个进程负责处理一部分文件,充分利用多核CPU资源提高整体处理速度。
关键代码片段
1. 使用fs.createReadStream
分块读取文件
const fs = require('fs');
const path = require('path');
function processFile(filePath) {
return new Promise((resolve, reject) => {
const readStream = fs.createReadStream(filePath, {
encoding: 'utf8',
highWaterMark: 1024 * 1024 // 每次读取1MB
});
let data = '';
readStream.on('data', (chunk) => {
data += chunk;
// 在这里进行复杂处理,例如正则表达式匹配
// 假设处理函数为 complexProcess
complexProcess(data);
});
readStream.on('end', () => {
resolve();
});
readStream.on('error', (err) => {
reject(err);
});
});
}
2. 使用cluster
模块开启多个工作进程
const cluster = require('cluster');
const os = require('os');
const { promisify } = require('util');
const fs = require('fs');
const path = require('path');
if (cluster.isMaster) {
const cpuCount = os.cpus().length;
for (let i = 0; i < cpuCount; i++) {
cluster.fork();
}
cluster.on('exit', (worker, code, signal) => {
console.log(`worker ${worker.process.pid} died`);
cluster.fork();
});
} else {
async function readDir(dirPath) {
const files = await promisify(fs.readdir)(dirPath);
const filePaths = files.map(file => path.join(dirPath, file));
for (const filePath of filePaths) {
await processFile(filePath);
}
}
readDir('/your/directory/path');
}
性能优化分析
- 分块读取:减少了内存占用,每次只在内存中处理一部分文件内容,避免因一次性读取大量文件内容导致内存溢出。
- 事件循环:非阻塞I/O操作允许在等待文件读取的同时执行其他任务,提高了CPU的利用率,使系统在高并发场景下能够更高效地处理任务,避免响应过慢。
- 集群(Cluster):通过开启多个工作进程,充分利用多核CPU资源,将文件处理任务分散到不同的进程中并行执行,显著提高了整体的处理速度,减少了处理10万个小文件所需的总时间。