在node.js中,我有一个读取流,我希望重新格式化并写入数据库。由于读取流速度很快且写入速度很慢,因此当写入队列建立起来时,node.js队列可能会被淹没(假设流是gb的数据)。如何强制读取等待代码的写入部分,这样不会在没有阻塞的情况下发生?
var request = http.get({
host: 'api.geonames.org',
port: 80,
path: '/children?' + qs.stringify({
geonameId: geonameId,
username: "demo"
})
}).on('response', function(response) {
response.setEncoding('utf8');
var xml = new XmlStream(response, 'utf8');
xml.on('endElement: geoname ', function(input) {
console.log('geoname');
var output = new Object();
output.Name = input.name;
output.lat = input.lat;
output.lng = input.lng;
output._key = input.geonameId;
data.db.document.create(output, data.doc, function(callback){
//this is really slow.
}
// i do not want to return from here and receive more data until the 'create' above has completed
});
});
答案 0 :(得分:3)
我昨晚遇到了这个问题,在我的黑客马拉松诱导睡眠不足状态下,我就是这样解决的:
每当我发出一个要处理的作业时,我会递增一个计数器,并在操作完成时递减计数器。为了防止出站流量压倒其他服务,我会在有一定数量的待处理出站请求时暂停流。代码与以下内容非常相似。
var instream = fs.createReadStream('./combined.csv');
var outstream = new stream;
var inProcess = 0;
var paused = false;
var rl = readline.createInterface(instream, outstream);
rl.on('line', function(line) {
inProcess++;
if(inProcess > 100) {
console.log('pausing input to clear queue');
rl.pause();
paused = true;
}
someService.doSomethingSlow(line, function() {
inProcess--;
if(paused && inProcess < 10) {
console.log('resuming stream');
paused = false;
rl.resume();
}
if (err) throw err;
});
});
rl.on('end', function() {
rl.close();
});
不是最优雅的解决方案,但它起作用并允许我处理数百万行而不会耗尽内存或限制其他服务。
答案 1 :(得分:0)
我的解决方案只是扩展了一个空的stream.Writable
,与@Timothy的基本相同,但是使用了事件和
不依赖于Streams1 .pause()
和.resume()
(这似乎对我的数据管道没有任何影响,
反正)。
var stream = require("stream");
var liveRequests = 0;
var maxLiveRequests = 100;
var streamPaused = false;
var requestClient = new stream.Writable();
function requestCompleted(){
liveRequests--;
if(streamPaused && liveRequests < maxLiveRequests){
streamPaused = false;
requestClient.emit("resumeStream");
}
}
requestClient._write = function (data, enc, next){
makeRequest(data, requestCompleted);
liveRequests++;
if(liveRequests >= maxLiveRequests){
streamPaused = true;
requestClient.once("resumeStream", function resume(){
next();
});
}
else {
next();
}
};
计数器liveRequests
跟踪并发请求的数量,并随时递增
makeRequest()
在完成时调用并递减(即,requestCompleted()
)。如果请求有
刚刚制作完成且liveRequests
超过maxLiveRequests
,我们会使用streamPaused
暂停该流。如果有请求
完成后,流暂停,liveRequests
现在小于maxLiveRequests
,我们可以恢复流。以来
_write()
调用其next()
回调时,后续数据项会被读取,我们可以简单地推迟后者
我们的自定义"resumeStream"
事件的事件监听器,它模仿暂停/恢复。
现在,只需readStream.pipe(requestClient)
。
编辑:我在package中抽象了这个解决方案以及输入数据的自动批处理。