2015-11-26 42 views
7

我有一個Elixir/Phoenix服務器應用程序,客戶端通過websockets通過內置通道系統連接。現在我想檢測用戶何時離開頻道。如何檢測用戶是否因網絡斷開而離開Phoenix頻道?

旁註:我在Google Chrome擴展中使用JavaScript客戶端庫。爲此,我從Phoenix提取了ES6代碼,將其轉換爲javascript,並稍微調整了一下,以便它可以獨立運行。

現在當我剛剛關閉彈出窗口時,服務器立即觸發terminate/2函數reason = {:shutdown, :closed}。在擴展端沒有任何類型的關閉回調,所以這太棒了!

但是,當客戶端只是丟失網絡連接(我連接第二臺計算機,只是拔出網絡插頭),然後terminate/2將不會觸發。

爲什麼以及如何解決這個問題?

我玩過timeout選項transport :websocket, Phoenix.Transports.WebSocket,但這並沒有解決。

更新: 隨着新真棒鳳凰1.2 Presence的東西,這不應該不再需要。

+0

我剛剛注意到,服務器並不總是識別彈出窗口關閉時。所以我希望我的問題的解決方案也能解決這個問題。 –

回答

16

這樣做的正確方法是不要在您的頻道中捕捉退出,而是讓另一個進程監視您。當你下樓時,它可以調用回調。下面是一個片段,讓你開始:

# 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(channels, pid, mfa)}} 
    end 
end 
+1

我得到它的工作,我有兩個後續問題:1.爲什麼不是核心(這太好了)? 2.網絡斷開和離開觸發之間的時間約爲90秒。這是以任何方式定製? (我想將傳輸超時設置爲20秒,並且每隔10秒就會ping服務器......但是當然會有額外的資源被燒燬) –

相關問題