1

我正在使用elixir-socket库作为将我的后端应用程序连接到外部 websocket 的一种方式。我需要管理此过程(如果出现故障则重新启动,如果无法连接则以指数方式回退等)。

目前,我创建了一个管理 GenServer 进程,该进程在给定时间后生成一个循环套接字(如下所示)。我有一个主管管理SocketManager(以及链接的Socket)流程:

socket_manager.ex

defmodule MyApp.SocketManager do
  def init(_) do
    Process.flag(:trap_exit, true)
    state = %{socket: nil}
    {:ok, state, {:continue, :init}}
  end

  def handle_continue(:init, state) do
    Task.start_link(fn ->
      Socket.connect!()
    end)
    {:noreply, state}
  end
end

套接字

defmodule MyApp.Socket do
  def connect! do
    socket = Socket.Web.connect!("xx.xx.com", secure: true, path: "/api/xxx")
    SocketManager.socket_connected(socket) # save the socket in the SocketManager state
    listen(socket)
  end

  defp listen(socket) do
    case socket |> Socket.Web.recv!() do
      {:text, data} ->
        # handle message
      {:close, :abnormal, _} ->
        Process.exit(self(), :kill)
      {:pong, _} ->
        nil
    end
    listen(socket)
  end
end

上面的效果很好,但是我不确定这是构造它的最佳方法。据我了解,Task应该只针对具有确定生命周期的任务,而不是针对永久过程。此外,在运行时,mix dialyzer我得到以下输出(参考 中的Task.spawn_linkSocketManager):

lib/myapp/socket_manager.ex:40:no_return
The created fun has no local return.

任何人都可以帮助我就如何构建它以及我如何能够满足 Dialyzer 提出建议?

谢谢!

4

1 回答 1

0

如果其他人有兴趣,这就是我最终的结果。我认为这是一个稍微好一点的结构,尽管可能有更好/更惯用的方式。它使用 DynamicSupervisor 来监督套接字进程。它也不再尝试连接时进程

socket_manager.ex

defmodule MyApp.SocketManager do
  def start_link(_) do
    GenServer.start_link(__MODULE__, [], name: __MODULE__)
  end

  def connect do
    GenServer.cast(__MODULE__, {:connect})
  end

  def handle_cast({:connect}, state) do
    spec = %{
      id: LiveSocket,
      start: {MyApp.Socket, :connect, []},
      type: :worker
    }
    {:ok, pid} = DynamicSupervisor.start_child(MyApp.DynamicSupervisor, spec)
    Process.link(pid)
    {:noreply, state}
  end
end
于 2018-11-02T13:41:41.453 回答