在 UserSocket 上跟踪状态
Track presence on UserSocket
我正在尝试使用 Phoenix.Presence
跟踪用户在 UserSocker
上的存在,而不需要客户端出现在特定频道上。
稍后我想订阅一个用户的存在
在不同的渠道中了解用户的存在。
除了在用户断开连接时收到 presence_diff
消息外,我可以正常工作。我正在做的是跟踪 UserSocket
每个用户在不同主题上的存在:
defmodule MyAppWeb.UserSocket do
# ...
def connect(%{"user_id" => user_id, "password" => password}, socket) do
case Accounts.authenticate(user_id, password) do
{:ok, user} ->
track_user_presence(socket.transport_pid, user)
{:ok, assign(socket, :user, user)}
_error -> :error
end
end
defp user_presence_topic(user_id) do
"user_presence:#{user_id}"
end
defp track_user_presence(pid, user) do
MyApp.Presence.track(pid, user_presence_topic(user.id), user.id, %{
online_at: inspect(System.system_time(:seconds))
})
end
end
我正在从我的频道订阅用户不同的状态主题:
defmodule MyAppWeb.RoomChannel do
# ...
def join("room:" <> room_id, payload, socket) do
send(self(), :after_join)
{:ok, assign(socket, :room_id, room_id)}
end
def handle_info(:after_join, socket) do
user_ids = ~w(1 2)
presence_state = get_and_subscribe_to_user_presence(socket, user_ids)
push(socket, "presence_state", presence_state)
{:noreply, socket}
end
def get_and_subscribe_to_user_presence(socket, user_ids) do
user_ids
|> Enum.map(&user_presence_topic/1)
|> Enum.map(fn topic ->
Phoenix.PubSub.subscribe(
socket.pubsub_server,
topic,
fastlane: {socket.transport_pid, socket.serializer, []}
)
Presence.list(topic)
end)
|> Enum.reduce(%{}, fn map, acc -> Map.merge(acc, map) end)
end
defp user_presence_topic(user_id) do
"user_presence:#{user_id}"
end
end
我得出结论,我需要以某种方式监视套接字 transport_pid
并在套接字终止时自己发送存在差异。
另一个想法是将客户端从 UserSocket.connect/2
功能加入到一个单独的状态通道,但到目前为止我还没有找到如何存档。
我一起破解了一个简单的 phoenix 应用程序,并通过测试概述了问题:https://github.com/kbredemeier/socket_presence
如有任何建议,我们将不胜感激。
想通了。而不是使用 PubSub.subscribe/3
我最终使用了
MyApp.Endpoint.subscribe/1
与我想订阅的主题并在我的订阅频道上实施 handle_info/2
。 handle_info/2
然后将从订阅的频道接收广播,在我的例子中是 presence_diff
s。
Chris McCord 在闲暇时向我指出,Phoenix.PubSub.subscribe/3
的 fastlane
选项允许在广播时向与订阅者不同的进程发送消息。在我的例子中,这导致我的客户收到的 presence_diff
不是我期望的频道。
我正在尝试使用 Phoenix.Presence
跟踪用户在 UserSocker
上的存在,而不需要客户端出现在特定频道上。
稍后我想订阅一个用户的存在 在不同的渠道中了解用户的存在。
除了在用户断开连接时收到 presence_diff
消息外,我可以正常工作。我正在做的是跟踪 UserSocket
每个用户在不同主题上的存在:
defmodule MyAppWeb.UserSocket do
# ...
def connect(%{"user_id" => user_id, "password" => password}, socket) do
case Accounts.authenticate(user_id, password) do
{:ok, user} ->
track_user_presence(socket.transport_pid, user)
{:ok, assign(socket, :user, user)}
_error -> :error
end
end
defp user_presence_topic(user_id) do
"user_presence:#{user_id}"
end
defp track_user_presence(pid, user) do
MyApp.Presence.track(pid, user_presence_topic(user.id), user.id, %{
online_at: inspect(System.system_time(:seconds))
})
end
end
我正在从我的频道订阅用户不同的状态主题:
defmodule MyAppWeb.RoomChannel do
# ...
def join("room:" <> room_id, payload, socket) do
send(self(), :after_join)
{:ok, assign(socket, :room_id, room_id)}
end
def handle_info(:after_join, socket) do
user_ids = ~w(1 2)
presence_state = get_and_subscribe_to_user_presence(socket, user_ids)
push(socket, "presence_state", presence_state)
{:noreply, socket}
end
def get_and_subscribe_to_user_presence(socket, user_ids) do
user_ids
|> Enum.map(&user_presence_topic/1)
|> Enum.map(fn topic ->
Phoenix.PubSub.subscribe(
socket.pubsub_server,
topic,
fastlane: {socket.transport_pid, socket.serializer, []}
)
Presence.list(topic)
end)
|> Enum.reduce(%{}, fn map, acc -> Map.merge(acc, map) end)
end
defp user_presence_topic(user_id) do
"user_presence:#{user_id}"
end
end
我得出结论,我需要以某种方式监视套接字 transport_pid
并在套接字终止时自己发送存在差异。
另一个想法是将客户端从 UserSocket.connect/2
功能加入到一个单独的状态通道,但到目前为止我还没有找到如何存档。
我一起破解了一个简单的 phoenix 应用程序,并通过测试概述了问题:https://github.com/kbredemeier/socket_presence
如有任何建议,我们将不胜感激。
想通了。而不是使用 PubSub.subscribe/3
我最终使用了
MyApp.Endpoint.subscribe/1
与我想订阅的主题并在我的订阅频道上实施 handle_info/2
。 handle_info/2
然后将从订阅的频道接收广播,在我的例子中是 presence_diff
s。
Chris McCord 在闲暇时向我指出,Phoenix.PubSub.subscribe/3
的 fastlane
选项允许在广播时向与订阅者不同的进程发送消息。在我的例子中,这导致我的客户收到的 presence_diff
不是我期望的频道。