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

when to disconnect and when to end a pg client or pool

我的栈是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();
    })
});

在 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 (,)',[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= ',[username])
      .then(res => {
        client.end();
        if (res.rows.length == 0) {
          return resolved(true);
        }
        resolved(false);
      })
      .catch(e => {
        client.end();
        console.error('error ', e);
      });
  })
}

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

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

谢谢

很简单,打开一个client-connection(单连接),用它查询,查询完就结束。

mysql 的情况下,池的概念是不同的:您必须在完成后将连接 .release() 返回到池中,但似乎 pg 是一个不同的故事:

From an issue on the github repo : Cannot use a pool after calling end on the pool #1635

"Cannot use a pool after calling end on the pool"

You can't reuse a pool after it has been closed (i.e. after calling the .end() function). You would need to recreate the pool and discard the old one.

The simplest way to deal with pooling in a Lambda is to not do it at all. Have your database interactions create their own connections and close them when they're done. You can't maintain a pool across freeze/thaw cycles anyway as the underlying TCP sockets would be closed.

If opening/closing the connections becomes a performance issue then look into setting up an external pool like pgbouncer.

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

您不应该在每次查询时断开连接池,连接池应该用于 "hot" 个连接。

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

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

如果您不想管理 open/close connections/pool 或发布,您可以尝试 https://github.com/vitaly-t/pg-promise,它会默默地管理所有这些东西并且运行良好。

首先,来自pg documentation*:

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]) // 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
      })
  })

这个 code/construct 足够/让你的游泳池工作,提供你在这里的东西。如果您关闭您的应用程序,连接将正常挂起,因为池创建良好,完全不会挂起,即使它确实提供了手动挂起方式, 请参阅 article 的最后一节。 还要看看前面的红色部分,上面写着 "You must always return the client..." to accept

  • 强制性client.release()指令
  • 在访问参数之前。
  • 您 scope/closure 客户在您的回调中。

然后,从pg.client documentation*:

带有承诺的纯文本查询

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())

在我看来最清晰的语法:

  • 无论结果如何,你都结束了客户端。
  • 您在 结束 客户端之前访问结果。
  • 您不 scope/closure 客户端在您的回调中

正是这两种语法之间的这种对立,乍一看可能会让人混淆,但其中并没有什么魔力,它是实现构造语法。 专注于您的 回调和查询,而不是那些构造,只需挑选最适合您的眼睛并用您的 代码喂养它。

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

关于 node-postgres 的 github 的文档说:

pro tip: unless you need to run a transaction (which requires a single client for multiple queries) or you have some other edge case like streaming rows or using a cursor you should almost always just use pool.query. Its easy, it does the right thing ™️, and wont ever forget to return clients back to the pool after the query is done.

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

var pool = new Pool()

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

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

})

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