使用 ActionCable 仅广播到房间中的一个网络套接字

Broadcast to only one web socket in room using ActionCable

我正在使用 ActionCable 来提供易于实现的浏览器通知。当用户在浏览器中打开多个选项卡时,问题就来了,我只需要将数据发送到一个选项卡,即用户房间中只有一个 WebSocket。如何做到这一点?

使用 pub/sub 方法无法直接隔离单个 pub/sub 客户端,因为整个想法是发布者不知道订阅者。

不过,有两种常见的方法可以解决这个问题:

  1. 为每个连接使用不同的命名通道,将命名通道保存在数据库中并将所有消息转发到该特定命名通道(即在数据库中保存通道 user-ID-TIMESTAMP 并使用它作为目标连接)。

  2. 另一种更可靠的方法(虽然更复杂)是将消息发送给所有客户端,但创建一个竞争条件,只允许单个客户端接收实际消息。这看起来像这样:

    • 服务器向所有客户端发送"you have a message"。
    • 客户端从服务器轮询 "undelivered" 条消息。
    • 服务器锁定消息池或使用数据库事务来检索未送达的消息并将消息标记为已送达。服务器将未送达的消息发送给客户端(可选地设置 ACK 超时)。
    • 单个客户端连接接收未送达的消息,其余的收到空消息数组(因为它们都已送达另一个客户端)或收到 "delivered" 标志,因此数据已更新但没有通知提出。
    • (可选)客户端发送并确认。
    • (可选)服务器将消息传递标记为完成。如果在 "timeout" 之前没有收到 ACK,服务器取消标记传递并重新发送 "you have a message" 消息。

祝你好运!

每次建立新连接时,我们都会创建一个新房间。因此,例如,当用户建立新连接时,我们可以将房间名称命名为 users:user_id:some_unique_random_string,这可能等于 users:user_id:123j123b1h2b1j23bh12b3,当同一用户通过打开另一个选项卡建立另一个连接时,我们也会这样做相同并创建一个单独的房间。 现在 ActionCable 提供的一件事是我们可以找到所有房间名称后跟任何前缀。

让用户建立了三个连接,他们的房间是users:128:123n1jh123ko9876,users:128:asdas23412cs1234,users:128:asni9202h5i3jens那么我们也可以使用ActionCable获取这些房间名称。

user_id = 128
pubsub = ActionCable.server.pubsub
channel_with_prefix = pubsub.send(:channel_with_prefix, RoomChannel.channel_name)
channels = pubsub.send(:redis_connection).pubsub('channels', "#{channel_with_prefix}:users:#{user_id}:*")

现在频道是一个由房间名称组成的数组。

所以puts channels

["chatapp_production:users:128:123n1jh123ko9876", "chatapp_production:users:128:asdas23412cs1234", "users:128:asni9202h5i3jens"]

这就是我们如何在不使用任何外部数据库和 API 调用的情况下找到与单个用户相关的所有房间的方法。