如何检测用户是否因网络断开而离开Phoenix通道?

Phi*_*ren 14 elixir websocket channels phoenix-framework

我有一个Elixir/Phoenix服务器应用程序,客户端通过websockets通过内置通道系统连接.现在我想检测用户何时离开频道.

旁注:我正在使用Google Chrome扩展程序中的javascript客户端库.为此,我从Phoenix中提取了ES6代码,将其转换为javascript,并稍微调整一下,使其独立运行.

现在当我关闭弹出窗口时,服务器立即触发该terminate/2功能reason = {:shutdown, :closed}.扩展端没有任何关闭回调,所以这很棒!

但是,当客户端只是失去网络连接(我连接第二台计算机,只是拉出网络插头),然后terminate/2将不会触发.

为什么以及如何解决此问题?

我玩了timeout选择,transport :websocket, Phoenix.Transports.WebSocket但这没有成功.

更新: 使用新的真棒凤凰1.2的Presence东西,这不再需要了.

Chr*_*ord 30

正确的方法是不要在您的频道中捕获退出,而是让另一个进程监视您.当你失败时,它可以调用回调.以下是一个可以帮助您入门的代码段:

# 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
Run Code Online (Sandbox Code Playgroud)

  • 我得到了它的工作,我有两个后续问题:1.为什么这不是核心(它太好了)?2.网络断开和离开触发之间的时间约为90秒.这是以任何方式定制的吗?(我想把传输超时设置为让我们说20秒并且每10秒ping一次服务器......当然还有额外的资源被烧掉) (4认同)