Ryan Bates在this episode中讨论推送通知时提到了Postgres的监听/通知功能,但我还没有找到任何关于如何在我的rails应用程序中实现监听/通知的提示.

下面是pg适配器内部wait_for_notify函数的文档,但我不知道它的具体用途.

我们需要直接点击pg适配器的connection变量吗?

推荐答案

使用wait_for_notify方法可以找到正确的位置,但由于ActiveRecord显然没有提供使用它的API,因此需要找到ActiveRecord用来与Postgre对话的底层PG::Connection对象(或者其中一个,如果您正在运行多线程设置).

一旦建立了连接,只需执行所需的LISTEN条语句,然后将一个块(以及可选的超时时间)传递到wait_for_notify.请注意,这将阻止当前线程,并独占Postgres连接,直到达到超时或出现NOTIFY(因此,您不希望在web请求中这样做).当另一个进程在您正在收听的其中一个频道上发出NOTIFY时,将使用三个参数调用该块——通知的频道、触发NOTIFY的Postgres后端的pid,以及伴随NOTIFY的有效负载(如果有).

我已经有一段时间没有使用ActiveRecord了,所以可能有一种更干净的方法来实现这一点,但在4.0.0中似乎可以正常工作.beta1:

# Be sure to check out a connection, so we stay thread-safe.
ActiveRecord::Base.connection_pool.with_connection do |connection|
  # connection is the ActiveRecord::ConnectionAdapters::PostgreSQLAdapter object
  conn = connection.instance_variable_get(:@connection)
  # conn is the underlying PG::Connection object, and exposes #wait_for_notify

  begin
    conn.async_exec "LISTEN channel1"
    conn.async_exec "LISTEN channel2"

    # This will block until a NOTIFY is issued on one of these two channels.
    conn.wait_for_notify do |channel, pid, payload|
      puts "Received a NOTIFY on channel #{channel}"
      puts "from PG backend #{pid}"
      puts "saying #{payload}"
    end

    # Note that you'll need to call wait_for_notify again if you want to pick
    # up further notifications. This time, bail out if we don't get a
    # notification within half a second.
    conn.wait_for_notify(0.5) do |channel, pid, payload|
      puts "Received a second NOTIFY on channel #{channel}"
      puts "from PG backend #{pid}"
      puts "saying #{payload}"
    end
  ensure
    # Don't want the connection to still be listening once we return
    # it to the pool - could result in weird behavior for the next
    # thread to check it out.
    conn.async_exec "UNLISTEN *"
  end
end

有关更一般用法的示例,请参见Sequel's implementation.

Edit to add:下面是对发生的事情的另一个描述.这可能不是幕后的确切实现,但它似乎足够好地描述了行为.

Postgres保留每个连接的通知列表.当你使用一个连接来执行LISTEN channel_name时,你告诉Postgres该通道上的任何通知都应该被推送到该连接的列表中(多个连接可以监听同一个通道,因此一个通知可能会被推送到多个列表中).一个连接可以同时连接到多个通道,其中任何一个通道的通知都会被推送到同一个列表中.

wait_for_notify所做的是从连接列表中弹出最早的通知,并将其信息传递给块——或者,如果列表为空,则Hibernate ,直到通知可用并对其执行相同操作(或者直到达到超时,在这种情况下,它只返回nil).由于wait_for_notify只处理一个通知,如果你想处理多个通知,你必须反复调用它.

当您UNLISTEN channel_nameUNLISTEN *时,Postgres将停止将这些通知推送到您连接的列表中,但已经推送到该列表中的通知将保留在那里,并且在下次调用时等待通知仍将返回它们.这可能会导致一个问题,即在wait_for_notify之后但在UNLISTEN之前累积的通知仍然存在,并且在另一个线程判断该连接时仍然存在.在这种情况下,在UNLISTEN之后,你可能想在短时间内拨打wait_for_notify,直到它返回零.但是,除非你为了许多不同的目的大量使用LISTENNOTIFY,否则这可能不值得担心.

我在上面的Sequel实现中添加了一个更好的链接,我建议大家看看.这很简单.

Postgresql相关问答推荐

如何高效地将行聚合到数组中,同时保留`NULL`来指示丢失的数据?

如何在docker-compose中使用docker secrets设置ConnectionString?

postgres vacuum 是否改进了我的查询计划

Power BI + Postgres:只读用户

gorm 不生成字符串列

如何在 PostgreSQL 的回归测试中测试 TYPE 发送和接收函数

PostgreSQL比较两个jsonb对象

Hibernate 将用户模型保存到 Postgres

当脚本工作时,Postgres now() 时间戳不会改变

如何为 JavaEE 应用程序中的 PostgreSQL 热备设置配置连接故障转移?

Postgres/psycopg2 - 插入字符串数组

PostgreSQL 可以对数组元素有唯一性约束吗?

将 PostgreSQL 配置为仅适用于 LOCALHOST 或指定的 ip + 端口

从 PostgreSQL 中的字段中提取数字

丢弃顺序和级联

Django:按月查询组

Postgresql varchar 是否使用 unicode 字符长度或 ASCII 字符长度计算?

用于将布尔列排序为 true、null、false 的 SQL

Postgresql 用随机值更新每一行

pg_restore 目录错误