На самом деле, маршрутизация сокета выполняется на основе, как определить ваши темы в модуле проектов сокета с channel
API. Для моего Slack clone я использую три канала. У меня есть канал уровня системы для обработки обновления присутствия, канала пользователя и канала комнаты.
Любой данный пользователь подписался на 0 или 1 канал. Тем не менее, пользователи могут быть подписаны на несколько каналов.
Для сообщений, выходящих в определенную комнату, я транслирую их по каналу комнаты.
Когда я обнаруживаю непрочитанные сообщения, уведомления или значки для конкретной комнаты, я использую пользовательский канал. В каждом пользовательском канале хранится список номеров, которые пользователь подписал (они указаны на боковой панели клиента).
Хитрость все это использует API, пара каналов, в основном intercept
, handle_out
, My.Endpoint.subscribe
и handle_info(%Broadcast{},socket)
.
- Я использую
intercept
, чтобы поймать передаваемые сообщения, которые я хочу либо игнорировать, либо манипулировать перед их отправкой.
- В канале пользователя, подписаться на события транслируются из канала номера
- При подписке вы получаете
handle_info
вызов с %Broadcast{}
структурой, которая включает в себя тему, событие, и полезную нагрузку транслируемого сообщения.
Вот пара части моего кода:
defmodule UcxChat.UserSocket do
use Phoenix.Socket
alias UcxChat.{User, Repo, MessageService, SideNavService}
require UcxChat.ChatConstants, as: CC
## Channels
channel CC.chan_room <> "*", UcxChat.RoomChannel # "ucxchat:"
channel CC.chan_user <> "*", UcxChat.UserChannel # "user:"
channel CC.chan_system <> "*", UcxChat.SystemChannel # "system:"
# ...
end
# user_channel.ex
# ...
intercept ["room:join", "room:leave", "room:mention", "user:state", "direct:new"]
#...
def handle_out("room:join", msg, socket) do
%{room: room} = msg
UserSocket.push_message_box(socket, socket.assigns.channel_id, socket.assigns.user_id)
update_rooms_list(socket)
clear_unreads(room, socket)
{:noreply, subscribe([room], socket)}
end
def handle_out("room:leave" = ev, msg, socket) do
%{room: room} = msg
debug ev, msg, "assigns: #{inspect socket.assigns}"
socket.endpoint.unsubscribe(CC.chan_room <> room)
update_rooms_list(socket)
{:noreply, assign(socket, :subscribed, List.delete(socket.assigns[:subscribed], room))}
end
# ...
defp subscribe(channels, socket) do
# debug inspect(channels), ""
Enum.reduce channels, socket, fn channel, acc ->
subscribed = acc.assigns[:subscribed]
if channel in subscribed do
acc
else
socket.endpoint.subscribe(CC.chan_room <> channel)
assign(acc, :subscribed, [channel | subscribed])
end
end
end
# ...
end
Я также использую user_channel для всех событий, связанных с конкретным пользователем, как состояние клиента, сообщения об ошибках и т.д.
Но от того, что я понять, один сокет обрабатывает несколько тем. Когда запрос приходит в handle_in, он не знает, какая тема принимает и отправляет сообщение. Трансляция происходит на уровне сокета. – ed1t
Я собирался ответить в этом комментарии, но он был слишком большой. Короче говоря, 'broadcast/3' происходит на уровне сокета, но сокет знает, какова его текущая тема, как говорит @Jason Harrelson. (Мне было весело нырять во внутреннюю часть канала, но это необязательно.) – ibgib