在 PG Listen/Notify 轮询的情况下关闭 JDBC 连接是个好主意吗?

Is it a good idea to close the JDBC connection in case of PG Listen/Notify polling?

我们在应用程序中使用连接池。虽然我知道我们应该关闭并根据需要获取连接,因为我们正在使用连接池。我通过接收 Postgres LISTEN 通知实现了缓存更新机制。该代码与 documentation.

给出的规范示例非常相似

代码中可以看到,在构造函数中发起了查询,重新使用了连接。当由于任何因素导致带外连接关闭时,这可能会造成问题。一种解决方案是在每次使用前获取连接,但如您所见,该语句仅在构造函数中执行一次,但我仍然可以在轮询中收到通知。因此,如果我每次都获得连接,它将迫使我为每次迭代(延迟后)重新发出语句。我不确定这是否是一项昂贵的操作。

这里的中间立场是什么?

class Listener extends Thread
{
private Connection conn;
private org.postgresql.PGConnection pgconn;

Listener(Connection conn) throws SQLException
{
    this.conn = conn;
    this.pgconn = conn.unwrap(org.postgresql.PGConnection.class);
    Statement stmt = conn.createStatement();
    stmt.execute("LISTEN mymessage");
    stmt.close();
}

public void run()
{
    try
    {
        while (true)
        {
            org.postgresql.PGNotification notifications[] = pgconn.getNotifications();

          
            if (notifications != null)
            {
                for (int i=0; i < notifications.length; i++){
                    //use notification
                }   
            }

            Thread.sleep(delay);
        }
    }
    catch (SQLException sqle)
    {
        //handle
    }
    catch (InterruptedException ie)
    {
        //handle
    }
}

}

除此之外,还有一个类似的document,除了构造函数之外,在运行方法中也有另一个查询。我想知道是否有人可以启发我该方法中另一个查询的目的。

public void run() {
    while (true) {
        try {
            //this query is additional to the one in the constructor
            Statement stmt = conn.createStatement();
            ResultSet rs = stmt.executeQuery("SELECT 1");
            rs.close();
            stmt.close();

            org.postgresql.PGNotification notifications[] = pgconn.getNotifications();
            if (notifications != null) {
                for (int i=0; i<notifications.length; i++) {
                    System.out.println("Got notification: " + notifications[i].getName());
                }
            }

            // wait a while before checking again for new
            // notifications
            Thread.sleep(delay);
        } catch (SQLException sqle) {
            //handle
        } catch (InterruptedException ie) {
            //handle
        }
    }
}

我尝试在每次迭代中关闭连接(但没有得到另一个)。那仍然有效。可能是因为解包完成了。

堆栈: Spring 启动、JPA、Hikari、Postgres JDBC 驱动程序(不是 pgjdbc-ng)

连接池是仆人,不是主人。只要您使用它来收听,就保持连接,即理想情况下永远保持连接。如果连接确实关闭,那么您将错过关闭时发送的任何通知。因此,为了保持缓存的良好状态,您需要丢弃整个缓存并重新开始。显然这不是您想要定期做的事情,或者首先拥有它的意义何在?

您显示的另一个文档只是第一个文档的旧版本。轮询之前的虚拟查询用于检查底层套接字代码以确保它已吸收所有消息。这不再是必要的。我不知道它是否有必要,它可能只是一些货物崇拜进入了文档。

使用此代码的阻塞版本可能会更好,方法是使用 getNotifications(0) 并删除 sleep(delay)。这将阻塞直到通知可用,而不是每秒唤醒两次并在再次睡眠之前消耗一些(少量)资源。此外,一旦通知确实到达,它几乎会立即得到处理,而不是等待 half-second 超时的剩余时间到期(因此,平均而言,大约四分之一秒)。