【问题标题】:when to disconnect and when to end a pg client or pool何时断开连接以及何时结束 pg 客户端或池
【发布时间】:2018-11-02 23:41:21
【问题描述】:

我的堆栈是 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 的路由中,我使用客户端而不是具有与池不同的 db 权限的池。

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);
      });
  })
}

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

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

谢谢

【问题讨论】:

  • 之前的解决方案有帮助吗?
  • 如果您使用自动管理连接的pg-promise,您就不会遇到这个问题。当您尝试像这样在低级别管理连接时,您最终可能会做错事、泄漏连接和/或损害应用程序。
  • @vitaly-t 你是 pg-promise 的创造者,对吧?只是几个问题。它在后台使用什么,客户端或池?或者没关系,因为这是内部解决的?另外,我可以在同一个应用程序中有多个 pg-promises,每个都连接到同一个数据库,使用不同的配置对象吗? (那些配置对象,每个都有不同的用户,拥有不同的数据库权限。)谢谢
  • @slevin pg-promise 在每个Database 对象下使用Pool,但它还允许您在需要时访问独占的Client,例如LISTEN/NOTIFY。对于您的其余问题 - 是的,是的,是的;)
  • @vitaly-t 酷。另外,我猜当使用事务时,pg-promise 使用 Clients 在后台处理它? (因为 pg 确实强调了在使用事务时,您必须使用客户端)。实际上 pg-promise 看起来很简单,我将对其进行测试,因为与我处理 pg 连接和客户端/池的尝试相比,它是高级别的。谢谢

标签: node.js postgresql node-postgres


【解决方案1】:

很简单,打开一个客户端连接(单连接),用它查询,完成后结束它。

池的概念是不同的,在 mysql 的情况下:一旦你完成它,你必须 .release() 连接回池,但似乎 pg 是一个不同的故事:

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

“在池上调用 end 后无法使用池”

您不能在池关闭后重用它(即在调用 .end() 函数)。您需要重新创建池并丢弃 旧的。

在 Lambda 中处理池化的最简单方法是不要在 全部。 让您的数据库交互创建自己的连接并 完成后关闭它们。 你不能在 无论如何,冻结/解冻循环,因为底层 TCP 套接字将是 关闭。

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

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

【讨论】:

  • 我不知道为什么这个答案是-1。
【解决方案2】:

您不应该在每次查询时断开连接池,连接池应该用于“热”连接。

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

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

如果您不想管理打开/关闭连接/池或释放,您可以尝试https://github.com/vitaly-t/pg-promise,它会静默管理所有这些内容并且效果很好。

【讨论】:

    【解决方案3】:

    首先,来自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', [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
          })
      })
    

    此代码/构造足够/使您的池正常工作,提供您的东西。如果您关闭应用程序,连接将正常挂起,因为池创建得很好,完全不会挂起,即使它确实提供了手动挂起方式, 请参阅article 的最后一部分。 另请查看前面的红色部分,上面写着“您必须始终返回客户......”以接受

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

    那么,来自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())
    

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

    • 不管结果如何,你都会结束客户端。
    • 您在结束客户端之前访问结果。
    • 您不会在回调中限定/关闭客户端

    这两种语法之间的这种对立乍一看可能会让人感到困惑,但其中并没有什么神奇之处,它是实现构造语法。 专注于您的回调和查询,而不是那些构造,只需挑选最适合您的眼睛并用您的代码提供它。

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

    【讨论】:

    • 你是救生员,朋友。我错过了这个版本的连接,这给我带来了麻烦。非常感谢!
    • const { Client } = require('pg').Client -> const { Client } = require('pg')
    【解决方案4】:

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-06-17
      • 1970-01-01
      • 2017-12-28
      • 1970-01-01
      • 2019-01-29
      • 2012-05-17
      • 1970-01-01
      • 2010-11-01
      相关资源
      最近更新 更多