Elixir和Phoenix的背景流程

时间:2018-01-19 18:02:41

标签: elixir phoenix-framework background-process

我有一个创建Question并将此问题插入数据库的系统,我点击了我设置的链接。这是凤凰城的简单话题。创建控制器操作为其设置链接,然后单击按钮以触发该操作。

这适用于现在。但下一阶段是让系统在没有任何UI干预的情况下创建问题。所以,这让我进入了Elixir / Phoenix的新地方。我的新问题是:我需要在x时刻自动运行此功能。

问题:

在Elixir / Phoenix中实施后台任务的最惯用方法是什么?我对Genserver或Supervisors知之甚少,但我想我已经准备好开始学习这些工具了。尽管如此,你将如何解决将逻辑转移到后台工作的问题。

CODE:

  def build_question(conn, _params) do
    case Questions.create_total_points_question(conn) do
      {:ok, _question} ->
        conn
        |> put_flash(:info, "Question created successfully.")
        |> redirect(to: page_path(conn, :index))
      {:error, _msg} ->
        conn
        |> redirect(to: page_path(conn, :index))
    end
  end 

此控制器操作是从链接触发的。需要在后台调用此代码。感谢您的帮助。

2 个答案:

答案 0 :(得分:1)

你几乎没有选择。

一个是在您的操作中简单运行Task.async,但是将您执行操作的进程与您生成的进程相关联,因此任务崩溃将影响生成它并等待它的进程。顺便说一句,在你的特殊情况下,我不认为你在行动中想要这个,因为你在等待结果时没有任何工作要做,所以没有必要。

第二个选项,如果您不想等待结果,请使用Task.start_link。这没关系,但是再次作为启动功能的名称,任务进程与您的相关联,因此这两个中的任何一个崩溃都会导致其他崩溃。

第三个选项,是使用Task.Supervisor,只需打开您的应用程序ex文件(可能是一个在项目中有名称并且在lib文件夹中)并在children = [...列表下方添加如下此

children = [
    ...
    supervisor(Task.Supervisor,[], [name: MyApp.TaskSupervisor])
]

这将在您的应用中启动名称为MyApp.TaskSupervisor的超级用户进程,您可以调用该进程并告知要运行的代码并对其进行监督。

现在,第三个选项可让您在应用程序中获得更多控制权,因为:

  1. 您可以在没有链接过程的情况下运行任务异步(在后台),该过程指示主管应该执行什么任务和任务过程,而不是主管将监视此任务
  2. 你仍然可以等待结果
  3. 如果您希望在任务崩溃时进程崩溃,您仍然可以链接任务
  4. 您可以轻松地将任务分发到其他节点。
  5. 您可以在documentation

    中找到有关此内容的更多信息

答案 1 :(得分:0)

我遇到了完全相同的问题,所以我决定给出一个尽可能简单的具体答案。

  1. 为您的 def start(_type, _args) do # List all child processes to be supervised children = [ # ... # add the following line {Task.Supervisor, name: MyApp.TaskSupervisor} ] # ... 添加动态主管。 请参阅文档 here
Task.Supervisor.start_child(MyApp.TaskSupervisor, fn ->
  IO.puts "I am running in a task"
end)
  1. 从任何地方(例如控制器或上下文)运行任何类型的代码。 https://hexdocs.pm/elixir/Task.Supervisor.html#start_child/3
:temporary

请注意,在这种情况下,您不必等待任务。这也意味着错误处理取决于您。您可以在执行代码时应用选项(请参阅上一个链接)。默认的 def build_question(conn, _params) do Task.Supervisor.start_child(MyApp.TaskSupervisor, fn -> MyApp.Questions.create_total_points_question(conn) end) conn |> put_flash(:info, "Question is currently being processed.") |> redirect(to: page_path(conn, :index)) end 不会在失败时重新启动任务,其优点是不会因重复失败而使其父级(应用程序)崩溃。


现在,假设您不关心问题构建的结果,那么您的具体问题。我会像这样在控制器中实现它:

{{1}}

如果你在乎结果,那么这不是办法。在这种情况下,我建议使用 liveview。它可以:

  • 只需监听事件,执行操作并在准备好后立即返回结果(同时阻止用户操作,即使他看到了页面)。我不建议这样做。
  • 执行后台任务并收到 Endpoint 的通知。这比听起来容易得多。您基本上在挂载实时视图时订阅,并确保您的后台作业发布到端点。
  • 让您的异步任务在完成后向您发送消息。在这种情况下,您可以给它一个 pid 来通知。
  • 也许还有别的?我很确定这已经在其他地方得到了回答。