NodeJS 在多次请求后无限期挂起

Posted

技术标签:

【中文标题】NodeJS 在多次请求后无限期挂起【英文标题】:NodeJS hangs indefinitely after multiple requests 【发布时间】:2014-02-08 15:54:57 【问题描述】:

我有一个 nodeJS(v.0.10.23) 代理连接到 postgres 数据库(node-postgres 模块 v2.1.0),以及返回各种 json 数据的 pgpool-II。

过去,这是处理连接错误的方式:

var after = function(callback) 
    return function(err, queryResult) 
        if(err) 
            response.writeHead(500, _header);
            console.log("ERROR: 500");
            console.log(err);
            return response.end(JSON.stringify(error: err));
        
        callback(queryResult)
    
;

基本上它的作用是在不存在错误的情况下消耗响应。

可以在这里找到深入的解释:Node js - http.request() problems with connection pooling

使用上面的函数,我得到了这样的结果:

pg.connect(_conString, after(function(err, client, done) 
  client.query(sql, after(function(result) 
  ...
  done();
 

由于当函数被传递到 after()s 回调时上下文丢失,我失去了使用 pg.connect() 传递的先天 done() 方法的能力。

删除 after 解决了问题,但是,在适当的时候,随着相当多的客户端拉数据,节点将挂起,直到它被重置。

是否有不同的方式来使用各种异步响应?

或者可能是一种将 pg.connect 上下文传递给回调的方法?

【问题讨论】:

请提供更多代码并描述您想要完成的工作 其他部分代码主要是一些toJSON解析逻辑,应该不相关。节点突然无限期挂起,它没有崩溃但也没有响应。重置后,我们看到所有“挂起”的请求都被一次调用。 @Mike86 - 你能让 nodejs 与 pgpool 对话(通过 pg 模块)吗?我正在为此苦苦挣扎,你能告诉我你使用的是哪个 pg-pool 版本吗? 特别是您是否使用了参数化查询,这对我来说似乎是个问题 我当然有;您需要做的就是设置池并将您的 njs 连接字符串指向它。我正在做的是将我的 nodeJS 实例传递给一个 AJAX 获取,其中包含一些属性,这些属性稍后会映射到我想要的当前存储过程,以及它们相应的参数。然后相应地构建查询并通过 pg.connect( 传递到适当的 tcp://pgdburl:port/ ,该 tcp://pgdburl:port/ 被映射到配置为将请求传递到适当的 db 的 pgpool 实例 【参考方案1】:

见最后一个例子(使用工厂函数和传递响应) 使用工厂函数很有趣,因为它可以节省代码

app.get('/parallel',function(request,response,next)  

function err(err)

  console.log(err.stack);
  response.end('error'+err.stack);

function send(data)

  response.end(JSON.stringify(data))


pg.connect(config, function(err, client, done) 
  if(err)done();return err(err);
  client.query("SELECT * FROM NOW()", function(err, result) 
      done();
      if(err)return err (err);
      send(result);
    , 0)
  )
)

您可以尝试使用 colan 的 async to 来选择。

npm 安装异步

// an example using an object instead of an array

var x1=null;
async.series(// in javascript the creation order is preserved in Object.keys
one: function(callback)
    setTimeout(function()
        x1=3;
        callback(null, 1);
    , 200);
,
two: function(callback)
    console.log('select where x1='+x1);
    setTimeout(function()
        callback(null, 2);
    , 100);

,
function(err, results) // called instantly if there is an error
// results is now equal to: one: 1, two: 2
);

单选工厂回调

function makesenderr(response)
 return function senderr(err,ok)
 
  console.log(err.stack);
  response.end('error'+err.stack);
 


function makesendjson(response)
 return function sendjson(data)
 
  response.end(JSON.stringify(data))
 



function tryconn(err,ok)
  return function(errarg, client, done) 
   if(errarg)done();return err(errarg);
   ok(client,done);
  



function doneerrok(done,err,ok)
  return function(errarg, result) 
   done();
   if(errarg)return err(errarg);
   ok(result);
  


var async=require('async')

app.get('/foo',function(request,response,next)
 var senderr=makesenderr(response)
 var sendjson=makesendjson(response)    
 pg.connect(config, tryconn(senderr,function(client,done)
      client.query("SELECT one FROM t1",doneerrok(done,senderror,sendjson), 0)
 ))
)

使用工厂函数并传递响应

 function senderr(response,err)
 //add headers here
  console.log(err.stack);
  response.end('error'+err.stack);
 


 function sendjson(response,data)
 //add headers here
  response.end(JSON.stringify(data))
 


function tryconn(response,ok)
  return function(err, client, done) 
   if(err)done();return senderr(response,err);
   ok(client,done);
  


function donerespok(done,response,ok)
  return function(err, result) 
   done();
   if(err)return err(response,err);
   ok(response,result);
  


function respok(response,ok)
  return function(err, result) 
   done();
   if(err)return err(response,err);
   ok(response,result);
  


function donecb(done,cb) return function()done();cb.apply(this,arguments); 

var async=require('async')

app.get('/foo',function(request,response,next)  
 pg.connect(config, tryconn(response,function(client,done)
      client.query("SELECT one FROM t1",donerespok(done,response,sendjson), 0)
 ))
)

app.get('/series',function(request,response,next)  
 pg.connect(config, tryconn(response,function(client,done)

    var one=;
    async.series( 
        one: function(cb) 
                  client.query("SELECT one FROM t1", function(err, result) 
                   one=result;cb(err,result);
                  , 0)
             ,
        two: function(cb)
                  client.query("SELECT two FROM t2 where one="+one[0].one,cb, 0)
        
    ,  // results is now equal to: one: [one:1], two: [two:2,two:2]
    donerespok(done,response,sendjson) );

 ))
)

app.get('/parallel',function(request,response,next)  

    async.parallel( 
        one: function(cb) 
         pg.connect(config, tryconn(response,function(client,done)
              client.query("SELECT one FROM t1",donecb(done,cb), 0)
         ))
        ,
        two: function(cb)
         pg.connect(config, tryconn(response,function(client,done)
              client.query("SELECT two FROM t2",donecb(done,cb), 0)
         ))
        
    ,  // results is now equal to: one: [one:1,one:2], two: [two:1,two:2]
    respok(response,sendjson) );
)

【讨论】:

【参考方案2】:

好吧,你当然会失去done(),你永远不会在你的after()函数中将第三个参数传递给你的回调。

function after(cb) 
    return function() 
        // you're using this function in a context where the arguments
        // passed in could be anything. the only constant is that the first
        // argument is the error, if any
        if (arguments[0]) 
            response.writeHead(500, _header);
            console.log("ERROR: 500");
            console.log(err);
            return response.end(JSON.stringify(error: arguments[0]));
        
        // apply the entire argument list to the callback, without modification
        cb.apply(cb, arguments);
    ;

...这也修正了通过queryResult 变量传递client 的可疑约定。

【讨论】:

【参考方案3】:

首先,如果您还没有增加连接池,您应该增加连接池,这看起来您不希望限制为 6 个连接。您还应该为请求设置一个相当低的超时时间。

关于数据的上下文,你有没有考虑将pg.connect的this绑定到after函数?这将允许您在本地环境中访问 done。

pg.connect(_conString, after(function(err, client, done) 
  this.done = done;
  client.query(sql, (after(function(result) 
     ...
      this.done();
  ).bind(this));
);

Smashing 杂志前几天有一篇关于使用bind() 的好文章here

【讨论】:

您将this 绑定到after(),但您仍然没有为done 设置任何内容...它是空的。 你的权利,错过了。我认为总的来说,有更好的方法来实现他正在寻找的东西。我不确定@Mike86 认为done 函数在哪里被传入。也许他的意思是pg.connect(_conString, done, after... 实际上@jeremy 有一个观点,但是目前,我不太确定它会解决这个问题。我担心直到现在我们还没有一个合适的测试环境,所以这周我无法真正深入了解它。如果赏金失败,我将在一周内设置另一个,我将运行我的测试,这样你的帮助就不会白费。 P.S 我们的池是 32x4(每个核心 32 个连接),对于我们当前的用户量及其并发性来说,imo 应该绰绰有余 done() 通过 pg.connect 方法传递给回调,只是after() 函数忽略了它,因此我的答案中更新了after() 函数。

以上是关于NodeJS 在多次请求后无限期挂起的主要内容,如果未能解决你的问题,请参考以下文章

.NET HttpClient 在多次请求后挂起(除非 Fiddler 处于活动状态)

PyCurl 请求在执行时无限挂起

django manage.py runserver 在第二个请求后挂起(间歇性)

NodeJS 和 Electron - 后端的请求承诺冻结前端的 CSS 动画

如何允许无限制的发布请求 expressjs

Angular HTTPClient (HTTP) 请求永远挂起