我有一个Elixir / Phoenix服务器应用程序,客户端通过websockets通过内置通道系统连接。现在我想检测用户何时离开频道。
旁注:我正在使用Google Chrome扩展程序中的javascript客户端库。为此,我从Phoenix中提取了ES6代码,将其转换为javascript,并稍微调整一下,使其独立运行。
现在,当我关闭弹出窗口时,服务器会立即使用terminate/2
触发reason = {:shutdown, :closed}
功能。扩展端没有涉及任何关闭回调,所以这很棒!
但是当客户端只是丢失了网络连接(我连接了第二台计算机而只拔出网络插头)时,terminate/2
将不会触发。
为什么以及如何解决此问题?
我使用了timeout
的{{1}}选项,但这没有用。
更新
使用新的令人敬畏的凤凰1.2 transport :websocket, Phoenix.Transports.WebSocket
的东西,这不再需要了。
答案 0 :(得分:27)
执行此操作的正确方法是不要在您的频道中捕获退出,而是让另一个进程监视您。当你失败时,它可以调用回调。以下是一个可以帮助您入门的代码:
# lib/my_app.ex
children = [
...
worker(ChannelWatcher, [:rooms])
]
# web/channels/room_channel.ex
def join("rooms:", <> id, params, socket) do
uid = socket.assigns.user_id]
:ok = ChannelWatcher.monitor(:rooms, self(), {__MODULE__, :leave, [id, uid]})
{:ok, socket}
end
def leave(room_id, user_id) do
# handle user leaving
end
# lib/my_app/channel_watcher.ex
defmodule ChannelWatcher do
use GenServer
## Client API
def monitor(server_name, pid, mfa) do
GenServer.call(server_name, {:monitor, pid, mfa})
end
def demonitor(server_name, pid) do
GenServer.call(server_name, {:demonitor, pid})
end
## Server API
def start_link(name) do
GenServer.start_link(__MODULE__, [], name: name)
end
def init(_) do
Process.flag(:trap_exit, true)
{:ok, %{channels: HashDict.new()}}
end
def handle_call({:monitor, pid, mfa}, _from, state) do
Process.link(pid)
{:reply, :ok, put_channel(state, pid, mfa)}
end
def handle_call({:demonitor, pid}, _from, state) do
case HashDict.fetch(state.channels, pid) do
:error -> {:reply, :ok, state}
{:ok, _mfa} ->
Process.unlink(pid)
{:reply, :ok, drop_channel(state, pid)}
end
end
def handle_info({:EXIT, pid, _reason}, state) do
case HashDict.fetch(state.channels, pid) do
:error -> {:noreply, state}
{:ok, {mod, func, args}} ->
Task.start_link(fn -> apply(mod, func, args) end)
{:noreply, drop_channel(state, pid)}
end
end
defp drop_channel(state, pid) do
%{state | channels: HashDict.delete(state.channels, pid)}
end
defp put_channel(state, pid, mfa) do
%{state | channels: HashDict.put(state.channels, pid, mfa)}
end
end