【问题标题】:NodeJS hangs indefinitely after multiple requestsNodeJS 在多次请求后无限期挂起
【发布时间】: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 实例

标签: sql node.js postgresql asynchronous callback


【解决方案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() 函数。
      猜你喜欢
      • 2018-09-02
      • 2014-01-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-05-19
      • 2018-03-24
      • 2012-03-31
      • 1970-01-01
      相关资源
      最近更新 更多