node.js进程在http.request循环中内存不足
在我的node.js服务器中,我不明白它为什么会耗尽内存。我的node.js服务器为它接收到的每个http请求发出一个远程http请求,因此我尝试用下面的示例脚本复制这个问题,该脚本也会耗尽内存 只有当for循环中的迭代次数非常高时,才会发生这种情况 在我看来,问题与node.js正在对远程http请求排队这一事实有关。如何避免这种情况 以下是示例脚本:node.js进程在http.request循环中内存不足,node.js,Node.js,在我的node.js服务器中,我不明白它为什么会耗尽内存。我的node.js服务器为它接收到的每个http请求发出一个远程http请求,因此我尝试用下面的示例脚本复制这个问题,该脚本也会耗尽内存 只有当for循环中的迭代次数非常高时,才会发生这种情况 在我看来,问题与node.js正在对远程http请求排队这一事实有关。如何避免这种情况 以下是示例脚本: (function() { var http, i, mypost, post_data; http = require('http'
(function() {
var http, i, mypost, post_data;
http = require('http');
post_data = 'signature=XXX%7CPSFA%7Cxxxxx_value%7CMyclass%7CMysubclass%7CMxxxxx&schedule=schedule_name_6569&company=XXXX';
mypost = function(post_data, cb) {
var post_options, req;
post_options = {
host: 'myhost.com',
port: 8000,
path: '/set_xxxx',
method: 'POST',
headers: {
'Content-Length': post_data.length
}
};
req = http.request(post_options, function(res) {
var res_data;
res.setEncoding('utf-8');
res_data = '';
res.on('data', function(chunk) {
return res_data += chunk;
});
return res.on('end', function() {
return cb();
});
});
req.on('error', function(e) {
return console.debug('TM problem with request: ' + e.message);
});
req.write(post_data);
return req.end;
};
for (i = 1; i <= 1000000; i++) {
mypost(post_data, function() {});
}
}).call(this);
$ node -v
v0.4.9
$ node sample.js
FATAL ERROR: CALL_AND_RETRY_2 Allocation failed - process out of memory
(函数(){
var http,i,mypost,post_数据;
http=require('http');
post_数据='signature=XXX%7CPSFA%7cxxx_值%7CMyclass%7CMysubclass%7CMxxxxx&schedule=schedule_name_6569&company=XXXX';
mypost=函数(post_数据,cb){
var post_选项,要求;
post_选项={
主机:“myhost.com”,
港口:8000,
路径:'/set_xxxx',
方法:“POST”,
标题:{
“内容长度”:post_data.Length
}
};
req=http.request(post_选项、函数(res){
var-res_数据;
res.setEncoding('utf-8');
res_数据=“”;
res.on('data',函数(块){
返回resu data+=chunk;
});
返回res.on('end',function()){
返回cb();
});
});
请求开启('错误',功能(e){
返回console.debug('TM请求问题:'+e.message);
});
请求写入(post_数据);
返回请求结束;
};
对于(i=1;iyes),您试图在启动1000000个请求之前将其排队。此版本保留有限数量的请求(100):
函数do\u 1000000\u请求(cb)
{
num_active=0;
num_finished=0;
计划的数量=0;
函数计划()
{
同时(活动数量<100和计划数量<1000000){
num_active++;
num_调度的++;
mypost(函数(){
num_活动--;
num_finished++;
如果(num_finished==1000000)
{
cb();
返回;
}否则如果(计划的数量<1000000)
时间表();
});
}
}
}
do_1000000_请求(函数(){
console.log('done!');
});
该模块可以帮助您。有关更多详细信息,请参阅这篇文章(法语)限制进入服务器的请求流
通过在实例上设置maxConnections
属性,可以防止内置服务器及其HTTP/HTTPS变体过载。设置此属性将导致节点停止accept()
连接,并强制操作系统在侦听()时丢弃请求
backlog已满,应用程序已在处理maxConnections
请求
限制传出请求
有时,有必要限制传出请求,如问题中的示例脚本所示
直接使用节点或使用通用池
如问题所示,未经检查直接使用节点网络子系统可能会导致内存不足错误。节点池
之类的功能使主动池管理具有吸引力,但它并不能解决无约束排队的基本问题。原因是节点池
不提供任何fe返回有关客户端池状态的信息
更新:从v1.0.7开始,节点池包含一个受本文启发的补丁,该补丁将布尔返回值添加到acquire()
。以下部分中的代码不再是必需的,具有streams模式的示例是使用节点池的代码
破解抽象
如所示,可以通过显式跟踪队列大小并将队列代码与请求代码混合来实现解决方案:
var useExplicitThrottling=函数(){
活跃变量=0
剩余风险=10
var queueRequests=函数(){
同时(活动<2&--剩余>=0){
active++;
pool.acquire(函数(错误,客户端){
如果(错误){
console.log(“从池中获取错误”)
如果(--活动<2)队列请求()
返回
}
console.log(“使用客户端处理请求”+客户端)
setTimeout(函数(){
pool.release(客户端)
如果(--激活<2){
queueRequests()
}
}, 1000)
})
}
}
排队请求(10)
log(“完成!”)
}
借用streams模式
该模式是一种在节点中惯用的解决方案。流具有write
操作,当流无法缓冲更多数据时,该操作返回false
。相同的模式可应用于具有acquire()的池对象
当获取了最大数量的客户端时返回false
。当活动客户端的数量降至最大数量以下时,将发出一个drain
事件。池抽象将再次关闭,并且可以忽略对池大小的显式引用
var useStreams=函数(){
var queueRequests=函数(剩余){
var full=false
pool.once('drain',function(){
如果(剩余)队列请求(剩余)
})
而(!full&&--remaining>=0){
日志(“发送请求…”)
full=!pool.acquire(函数(错误,客户端){
如果(错误){
console.log(“从池中获取错误”)
返回
}
console.log(“使用客户端处理请求”+客户端)
setTimeout(pool.release,1000,客户端)
})
}
}
排队请求(10)
log(“完成!”)
}
纤维
一个替代的解决方案可以通过在队列的顶部提供一个阻塞抽象来实现。模块暴露在C++中实现。通过使用光纤,可以阻止执行上下文而不阻塞节点事件循环。
function do_1000000_req( cb )
{
num_active = 0;
num_finished = 0;
num_sheduled = 0;
function shedule()
{
while (num_active < 100 && num_sheduled < 1000000) {
num_active++;
num_sheduled++;
mypost(function() {
num_active--;
num_finished++;
if (num_finished == 1000000)
{
cb();
return;
} else if (num_sheduled < 1000000)
shedule();
});
}
}
}
do_1000000_req( function() {
console.log('done!');
});