Elixir任务 - 优雅关闭

alP*_*ino 6 shutdown task elixir

我有Elixir Task,需要一些时间(10秒).当应用程序升级时,Task.Supervisor尽管有以下情况,此任务仍被终止shutdown: 30000:

=SUPERVISOR REPORT==== 13-Aug-2015::00:03:09 ===
 Supervisor: {local,tasks_sup}
 Context:    child_terminated
 Reason:     killed
 Offender:   [{pid,<0.304.0>},
              {id,'Elixir.Task.Supervised'},
              {mfargs,{'Elixir.Task.Supervised',start_link,undefined}},
              {restart_type,temporary},
              {shutdown,30000},
              {child_type,worker}]
Run Code Online (Sandbox Code Playgroud)

在应用程序升级时,我不知道如何优雅地停止任务(等到任务完成).这是描述我的问题的代码:

defmodule MyApp do
  use Application

  def start(_, _) do
    MyApp.Supervisor.start_link([])
  end

end

defmodule MyApp.Supervisor do

  use Supervisor

  def start_link(state) do
    Supervisor.start_link(__MODULE__, state, name: __MODULE__)
  end

  def init(state) do
    children = [
      supervisor(Task.Supervisor, [[name: :tasks_sup, shutdown: 30000]]),
      worker(MyApp.Worker, [state], restart: :permanent)
    ]

    supervise(children, strategy: :one_for_one)
  end

end

defmodule MyApp.Worker do

  def start_link(state) do
    GenServer.start_link(__MODULE__, state, [name: MyApp.Worker])
  end

  def init(state) do
    {:ok, state}
  end

  def handle_call(:which_children, _, state) do
    children = [{Task.Supervisor, :tasks_sup, :supervisor, [Task.Supervisor]}]
    {:reply, children, state}
  end

  def handle_info({:task, data}, state) do
    Task.Supervisor.async(:tasks_sup, MyApp.TaskRunner, :perform, [data])
  end

  def handle_info(_, state) do
    {:noreply, state}
  end

end

defmodule MyApp.TaskRunner do

  def perform(data) do
    # some 10 secs job
  end

end
Run Code Online (Sandbox Code Playgroud)

是否有任何想法或假设如何等到MyApp.TaskRunner.perform完成然后允许停止任务?

对我来说如何处理任务并不重要:使用本机Elixir Task或通过自己的一些TaskProcessor模块.

Task.Supervisor.async将任务链接到调用者,这可能是一个问题.不过,我试了几次不同的情况下,与asyncstart_link每一次得到了相同的结果.我的最新测试是:

children = [
  supervisor(Task.Supervisor, [[name: :tasks_sup, shutdown: 30000]]),
  worker(MyApp.Worker, [state], restart: :permanent)
]

supervise(children, strategy: :one_for_one)
Run Code Online (Sandbox Code Playgroud)

Task.Supervisor.start_child(:tasks_sup, MyApp.TaskRunner, :perform, [data])
Run Code Online (Sandbox Code Playgroud)

工人在大约2-3秒后被杀死.

Jos*_*lim 1

这些链接可能会导致您的任务失败。因为工作人员调用Task.Supervisor.async,它会将任务链接到您的工作人员。Worker 的超时时间为 5000 毫秒,因此它将在 Supervisor 之前关闭,从而杀死任务。您可以通过设置早期报告来确认这一点。

顺便说一句,只有async当您稍后通过同一进程调用时才应该调用await,但这里的情况似乎并非如此。您可能应该Task.Supervisor.start_child改为调用(因此任务不会链接到调用者)。

您的函数被杀死的另一个可能原因是虚拟机仅保留最新的两个代码模块版本。如果您在短时间内升级两次,旧版本将被清除,并且其正在运行的进程将被终止。