gpt4 book ai didi

javascript - 为什么我的 NodeJS 脚本在 fs.readFile 和 fs.appendFile 处理大量文件时陷入困境。

转载 作者:行者123 更新时间:2023-11-30 10:20:33 25 4
gpt4 key购买 nike

我有一个包含大约 120k HTML 页面的文件夹,我需要打开这些页面(每个文件大约 70kb),使用 xPath 解析一些数据并将该数据附加到 .csv 文件。

下面是我的代码:

它应该从 parseFolder 中读取文件列表,遍历每个文件名,使用 fs.readFile 打开它,然后使用 jsdom 和 xpath 解析数据并使用 fs.appendFile 将其保存到 csv 文件。

前 100 个左右的文件似乎表现不错,但之后会逐渐变慢,消耗内存和 cpu,最终停滞。我有 16 GB 的内存,当我的内存使用量达到大约 7 GB 时,它似乎达到了某个极限。

我是 JS 和 Node 的新手,非常感谢任何指出我遗漏的帮助。

var fs = require('fs');
var jsdom = require('jsdom').jsdom;
var xpath = require('xpath');
var S = require('string');
var os = require('os');

ParserRules = {
saveFile: 'output.csv',
parseFolder: '/a/folder/with/120k/HTML/files',
fields: {
"field1": "//div[@class='field1']/text()",
}
};

start();

function start() {
console.log('Starting...');
fs.readdir(ParserRules.parseFolder, iterateFiles);
}

function iterateFiles(err, filesToParse) {
for (var i = 0; i < filesToParse.length; i++) {
file = ParserRules.parseFolder + '/' + filesToParse[i];
console.log('Beginning read of ' + file);
fs.readFile(file, {encoding: 'utf8'}, parseFile);
}
}

function parseFile(err, data) {
if (err == null) {
var jsdomDocument = jsdom(data);
var document = jsdomDocument.parentWindow.document;
getContent(document);
}
}

function getContent(document) {
fields = ParserRules.fields;
var csvRow = [];
for (var field in fields) {
try {
console.log('Looking for ' + field);
var nodes = xpath.select(fields[field], document);
for (var i = 0; i < nodes.length; i++) {
csvRow.push(getValue(nodes[i]));
}
} catch (err) {
console.log(err);
}
}
saveToCsv(csvRow, ParserRules.saveFile);
}

function getValue(node) {
if(node.nodeValue != null) {
toReturn = node.nodeValue;
} else {
newNode = $(node);
toReturn = newNode.html();
}
return toReturn;
}

function saveToCsv(object, filePath) {
console.log('Saving...');
if(object.length > 0) {
console.log('Row Exists, Saving...');
toString = S(object).toCSV().s + os.EOL;
fs.appendFile(filePath, toString, {encoding: 'utf8', flag: 'a'}, function(err){
if (err) {
console.log('Write Error: ' + err);
} else {
console.log('Saved ' + object);
}
});
}
}

最佳答案

Node.js 异步工作。

问题

因此您的代码结构方式会发生这种情况:

  1. iterateFiles 函数连续发出 120k 次 fs.readFile 调用,这导致 Node.js 将 120k 文件系统读取操作排队。

  2. 当读取操作完成时,Node.js 将为 fs.readFile 调用 120k 回调,并且每个回调都会发出一个 fs.appendFile 操作,这将导致 Node.js 排队 120k 文件系统写入操作。

  3. 最终 Node.js 将调用传递给 fs.appendFile 的 120k 回调。在这些写入操作完成之前,Node.js 必须挂起要写入的数据。

解决方案

对于这样的任务,我建议使用 fs 调用的同步版本:fs.readFileSyncfs.appendFileSync

在为 Web 服务器或以某种方式由事件驱动编写代码时,您不希望使用这些调用的同步版本,因为它们会导致您的应用程序阻塞。但是,如果您正在编写对数据进行批处理的代码(例如,像 shell 脚本一样运行的代码),则使用这些调用的同步版本会更简单。

插图

以下代码是您的代码的简化模型并说明了问题。它被设置为从 /tmp 中读取,因为它与任何文件源一样好。如果文件为空,我还将其设置为避免做任何比 parseFile 更进一步的工作。

var fs = require('fs');

var ParserRules = {
saveFile: 'output.csv',
parseFolder: '/tmp'
};

start();

function start() {
console.log('Starting...');
fs.readdir(ParserRules.parseFolder, iterateFiles);
}

function iterateFiles(err, filesToParse) {
for (var i = 0; i < filesToParse.length; i++) {
var file = ParserRules.parseFolder + '/' + filesToParse[i];
console.log('Beginning read of file number ' + i);
fs.readFile(file, {encoding: 'utf8'}, parseFile);
}
}

var parse_count = 0;
function parseFile(err, data) {
if (err)
return;

if (data.length) {
console.log("Parse: " + parse_count++);
getContent(data);
}
}

function getContent(data) {
saveToCsv(data, ParserRules.saveFile);
}

var save_count = 0;
function saveToCsv(data, filePath) {
fs.appendFile(filePath, data, {encoding: 'utf8', flag: 'a'},
function(err){
if (err) {
console.log('Write Error: ' + err);
} else {
console.log('Saved: ' + save_count++);
}
});
}

如果运行此代码,您会看到所有 Parse: 消息连续出现。然后仅在所有Parse: 消息输出后,您会得到Saved: 消息。所以你会看到类似的东西:

Beginning read of file number N
Beginning read of file number N+1
Parse: 0
Parse: 1
... more parse messages ...
Parse: 18
Parse: 19
Saved: 0
Saved: 1
... more saved messages...
Saved: 18
Saved: 19

这告诉你的是,在解析完所有文件之前,Node 不会开始保存。由于 Node 无法释放与文件关联的数据,直到它知道不会再次使用它---在这种情况下,这意味着直到文件被保存---然后在某个时候 Node 将至少占用 120,000 * 70kb 内存用于保存所有文件中的所有数据。

关于javascript - 为什么我的 NodeJS 脚本在 fs.readFile 和 fs.appendFile 处理大量文件时陷入困境。,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21583968/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com