Phoenix.Channel.push
第一次接触 phoenix 框架时, 照着官网的教程做了一个聊天网页. 服务器可以在 channel 里广播消息, 还可以 push 消息给单个用户. phoenix 究竟是如何做到的. 我们先看看该函数的定义
@doc """
Sends event to the socket.
The event's message must be a serializable map.
## Examples
iex> push socket, "new_message", %{id: 1, content: "hello"}
:ok
"""
def push(socket, event, message) do
%{transport_pid: transport_pid, topic: topic} = assert_joined!(socket)
Server.push(transport_pid, topic, event, message, socket.serializer)
end
首先, 调用了 assert_joined!/1
函数, 来看看它有什么用
defp assert_joined!(%Socket{joined: true} = socket) do
socket
end
defp assert_joined!(%Socket{joined: false}) do
raise """
`push`, `reply`, and `broadcast` can only be called after the socket has finished joining.
To push a message on join, send to self and handle in handle_info/2, ie:
def join(topic, auth_msg, socket) do
...
send(self, :after_join)
{:ok, socket}
end
def handle_info(:after_join, socket) do
push socket, "feed", %{list: feed_items(socket)}
{:noreply, socket}
end
"""
end
原来只是确认一下该 socket 是否已加入频道, 会返回一个很详细的错误提示, 教你如何在 join 时 push 消息, 也许是因为很多人提了这个问题吧.
从 socket 中获取到 transport_pid
, topic
和 serializer
之后, 调用了 Server.push
函数.
Phoenix.Channel.Server.push
@doc """
Pushes a message with the given topic, event and payload
to the given process.
"""
def push(pid, topic, event, payload, serializer)
when is_binary(topic) and is_binary(event) and is_map(payload) do
encoded_msg = serializer.encode!(%Message{topic: topic,
event: event,
payload: payload})
send pid, encoded_msg
:ok
end
def push(_, _, _, _, _), do: raise_invalid_message()
Server.push
函数将收到的 topic
, event
和 payload
编码之后, 发送给了 transport. 那么 transport 会对这些信息作何处理呢?
cowboy_websocket:handler_loop
我们先暂停一下, 说说客户端加入某个 channel 时究竟发生了什么. 当客户端与服务器的连接建立后, 会生成一个 transport 进程, 随后客户端加入某个 channel 时, transport 进程又会生成一个 channel 进程. 当客户端断开连接后, transport 进程会死亡, 其下的 channel 进程也会随之死去. 所以每个用户加入的每个 channel 都有一个独立的进程, 用于保存一个 %Phoenix.Socket{}
结构体.
让我们继续之前的线索, 通过 observer, 我们找到了 tansport
进程中正在运行的函数 -- cowboy_websocket:handler_loop
. 此后便是 cowboy 将消息通过 websocket 发送给客户端了.
总结
phoenix channel 中的消息 push 机制很简单, 与关注同一个 channel 的其他人没有关系. 进程关系如图
下回我们将看看 phoenix 是如何广播消息的.