如何如果用户离开凤凰通道由于网络断开检测?

问题描述 投票:12回答:1

我有一个花好月圆/凤凰服务器应用程序和客户端通过建立渠道体系然后通过WebSockets连接。现在我想,当一个用户离开通道来检测。

旁注:我使用的是谷歌Chrome扩展中的JavaScript客户端库。为此,我提取的ES6代码凤凰,它transpiled的JavaScript,和调整它一点,所以它单独运行。

现在,当我刚刚关闭弹出,服务器将立即触发与terminate/2reason = {:shutdown, :closed}功能。有没有一种涉及的伸侧接近回调的,所以这是伟大的!

但是,当客户端只是失去网络连接(我连接第二台电脑,只是掏出网络插头),那么terminate/2不会触发。

为什么?如何解决这个问题?

我打得四处timeouttransport :websocket, Phoenix.Transports.WebSocketoption但没有成功。

更新:随着新真棒凤凰1.2 Presence的东西,这不应该不再需要。

websocket elixir phoenix-framework channels
1个回答
29
投票

要做到这一点,正确的方法是在你的频道不陷阱退出,而是有另一个进程监视你。当你去,它可以调用回调。下面是一个片段,让你开始:

# 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
© www.soinside.com 2019 - 2024. All rights reserved.