什么时候断开连接以及何时结束pg客户端或池

sle*_*vin 24 postgresql node.js pg node-postgres

我的堆栈是node,express和pg模块.我真的试图通过文档和一些过时的教程来理解.我不知道何时以及如何断开连接并结束客户端.

对于某些路线,我决定使用游泳池.这是我的代码

const pool = new pg.Pool({
  user: 'pooluser',host: 'localhost',database: 'mydb',password: 'pooluser',port: 5432});

pool.on('error', (err, client) => {
  console.log('error ', err);  process.exit(-1);
});

app.get('/', (req, res)=>{
  pool.connect()
    .then(client => {
      return client.query('select ....')
            .then(resolved => {
              client.release();
              console.log(resolved.rows);
            })
            .catch(e => { 
              client.release();
              console.log('error', e);
            })
      pool.end();
    })
});
Run Code Online (Sandbox Code Playgroud)

在CMS的路由中,我使用客户端而不是具有与池不同的数据库权限的池.

const client = new pg.Client({
  user: 'clientuser',host: 'localhost',database: 'mydb',password: 'clientuser',port: 5432});    
client.connect();

const signup = (user) => {
  return new Promise((resolved, rejeted)=>{
    getUser(user.email)
    .then(getUserRes => {
      if (!getUserRes) {
        return resolved(false);
      }            
            client.query('insert into user(username, password) values ($1,$2)',[user.username,user.password])
              .then(queryRes => {
                client.end();
                resolved(true);
              })
              .catch(queryError => {
                client.end();
                rejeted('username already used');
              });
    })
    .catch(getUserError => {
      return rejeted('error');
    });
  }) 
};

const getUser = (username) => {
  return new Promise((resolved, rejeted)=>{
    client.query('select username from user WHERE username= $1',[username])
      .then(res => {
        client.end();
        if (res.rows.length == 0) {
          return resolved(true);
        }
        resolved(false);
      })
      .catch(e => {
        client.end();
        console.error('error ', e);
      });
  })
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,如果我得到一个username already used并尝试使用另一个用户名重新发布,则getUser永远不会启动的查询和页面挂起.如果我client.end();从两个函数中删除它,它将工作.

我很困惑,所以请建议如何以及何时断开连接并完全结束池或客户端.任何提示或解释或教程将不胜感激.

谢谢

luc*_*chi 12

首先,从pg文档*:

const { Pool } = require('pg')

const pool = new Pool()

// the pool with emit an error on behalf of any idle clients
// it contains if a backend error or network partition happens
pool.on('error', (err, client) => {
  console.error('Unexpected error on idle client', err) // your callback here
  process.exit(-1)
})

// promise - checkout a client
pool.connect()
  .then(client => {
    return client.query('SELECT * FROM users WHERE id = $1', [1]) // your query string here
      .then(res => {
        client.release()
        console.log(res.rows[0]) // your callback here
      })
      .catch(e => {
        client.release()
        console.log(err.stack) // your callback here
      })
  })
Run Code Online (Sandbox Code Playgroud)

这个代码/构造是足够的 /使你的池工作,在这里提供你的东西.如果关闭应用程序,连接将保持正常,因为池创建良好,完全不挂起,即使它确实提供了手动挂起方式,请参阅本文的最后一节.另请查看之前的红色部分,其中写着"您必须始终让客户......"才能接受

  • 强制性client.release()指令
  • 在访问参数之前.
  • 你回调中的范围/闭包客户端.

然后,从pg.client文档*:

带有承诺的纯文本查询

const { Client } = require('pg').Client
const client = new Client()
client.connect()
client.query('SELECT NOW()') // your query string here
  .then(result => console.log(result)) // your callback here
  .catch(e => console.error(e.stack)) // your callback here
  .then(() => client.end())
Run Code Online (Sandbox Code Playgroud)

在我看来,最清晰的语法:

  • 无论结果如何,你都会结束客户
  • 结束客户端之前访问结果.
  • 你不在回调中范围/关闭客户端

正是这两种语法之间的这种对立可能会让人感到困惑,但是那里没有魔力,它是实现构造语法.专注于你的回调和查询,而不是那些构造,只需为你的眼睛选择最优雅的,并用你的代码喂它.

*为了清楚起见,我在这里添加了评论//你的xxx

  • `const { Client } = require('pg').Client` -> `const { Client } = require('pg')` (2认同)

EMX*_*EMX 8

它非常简单,打开一个客户端连接(单个连接),用它查询,一旦完成就结束它。

池的概念是不同的,在以下情况下mysql:一旦使用完毕,您必须将连接释放回池,但似乎 withpg是一个不同的故事:

来自 github 存储库上的一个问题:在池上调用 end 后无法使用池 #1635

“在池上调用结束后无法使用池”

池关闭后(即调用 .end() 函数后),您无法重用它。您需要重新创建池并丢弃旧池。

在 Lambda 中处理池化的最简单方法就是根本不这样做。让您的数据库交互创建自己的连接并在完成后关闭它们。无论如何,您都无法跨冻结/解冻周期维护池,因为底层 TCP 套接字将被关闭。

如果打开/关闭连接成为性能问题,那么请考虑设置一个外部池,例如 pgbouncer。

所以我想说你最好的选择是不要结束池,除非你要关闭服务器


Sim*_*llo 7

您不应该在每个查询上断开池,连接池应该用于"热"连接.

我通常在启动时有一个全局连接,并且池连接关闭(如果)应用程序停止; 你只需要在每次查询结束时从池中释放连接,就像你已经做的那样,并且在signup函数中也使用相同的池.

有时我需要保留连接,我在查询函数中使用包装器,在执行查询之前检查连接是否处于活动状态,但这只是一个优化.

如果您不想管理打开/关闭连接/池或发布,您可以尝试https://github.com/vitaly-t/pg-promise,它可以无声地管理所有这些内容并且运行良好.


小智 5

node-postgres 的github 上的文档说:

专业提示:除非你需要运行一个事务(它需要一个客户端进行多个查询)或者你有一些其他的边缘情况,比如流式行或使用游标,你应该几乎总是只使用 pool.query。它很简单,它做对了™?,并且永远不会忘记在查询完成后将客户端返回到池中。

所以对于非事务性查询,调用下面的代码就足够了。

var pool = new Pool()

pool.query('select username from user WHERE username= $1',[username], function(err, res) {

  console.log(res.rows[0].username)

})
Run Code Online (Sandbox Code Playgroud)

通过使用pool.query,库将负责在查询完成后释放客户端。