Правильный способ структурировать Elixir GenServer с процессом прослушивания - PullRequest
0 голосов
/ 10 сентября 2018

Я использую библиотеку elixir-socket как способ подключения к моему внутреннему приложению к внешней веб-розетке. Мне нужно, чтобы этот процесс был управляемым (перезапустите, если что-то не получится, откройте экспоненциально, если он не сможет подключиться и т.д.)

В настоящее время я создал управляющий процесс 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

socket.ex

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_link в SocketManager):

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

Может ли кто-нибудь помочь мне с предложениями о том, как еще структурировать это и как я мог бы удовлетворить Dialyzer?

Спасибо!

1 Ответ

0 голосов
/ 02 ноября 2018

Если кому-то еще интересно, это то, чем я закончил. Я думаю, что это немного лучшая структура, хотя, возможно, есть лучшие / более идиоматические способы. Он использует 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...