在控制器内启动一个进程

时间:2016-09-29 10:59:26

标签: elixir phoenix-framework

考虑这样的模块:

defmodule Sampple do
  def start(nick, password, state \\ %State{}) do
    {:ok, client} = ExIrc.start_client!
    GenServer.start(__MODULE__,
      [%{state | client: client,
         nick: nick,
         pass: password
        }]
    )
  end
  def enter(nick, token) do
    Logger.debug "Sending message"
    start(nick, token)
  end
  #another irrelevant callbacks
  def handle_info({:joined, _}, state) do
    ExIrc.Client.msg(state.client,
      :privmsg,
      state.channel,
      "!enter")
    {:stop, :normal, state}
  end
end

这样的GenServer在Phoenix Copntroller中实例化如下:

defmodule Cgas.Controller do
  use Cgas.Web, :controller
  require Logger
  def enter(conn, _args) do
    Logger.debug inspect(get_session(conn, :login))
    %{"login" => login,
      "token" => token } = get_session(conn, :login)
    {:ok, pid} = Sample.enter(login, token)
    Logger.debug inspect(pid)
    json conn, %{"success" => "true "}
  end
end

如果从repl或非控制器进程调用enter,则没有问题,尽管从控制器内部调用它时,进程仍处于活动状态但不执行任何操作。此示例模块用作ExIRC处理程序。

1 个答案:

答案 0 :(得分:0)

<强>原因

首先,所有控制器都是独立的过程。这意味着当用户发出请求时,流程开始,当他收到响应时,流程就会消失。 通过使用start_link启动进程,您可以创建一个双向链接,使死亡事件传播到其他链接进程。这意味着如果你有这样的连接:

Cgas.Controller <----> Sample

这意味着,如果SampleCgas.Controller死亡,则两者都会死亡。根据控制器如何工作的内部系统,它意味着Sample只要客户端连接到服务器就会存在(通常为1-100毫秒)

这可能是你注意到它&#34;没有做任何事情的原因&#34;因为它的生命中没有足够的时间(可怜的东西)

<强>解决方案

您可以做的第一件事就是使用start/2而不是start_link/2启动流程。但是,它会为每个向服务器发出的请求创建一个流程,最有可能最终成为一群僵尸进程。

如果你真的坚持这样做,那么你可以给它一个时间来生活。像过程注册的东西和:
 # if client didn't make any new request in X seconds commit suicide :erlang.exit self, :'die!'

但通常情况下,它再次成为网络框架的火花和遗忘规则,并且使用像WebSockets这样的实时连接要好得多,并且只要连接存在就让流程生效。