Elixir long 运行 后台任务未完成,有时会崩溃

Elixir long running background task doesn't complete, sometimes crashes

作为一点背景知识,我有一个端点负责视频上传。我想要做的是将视频文件从请求复制到一个临时位置,并启动一个异步任务(ffmpeg shell 进程)以在后台对该视频进行转码,以便我的端点可以 return 一个 200 及时,并且响应不等待 ffmpeg 完成该视频的转码。

这是来自控制器的代码。

def create(conn, %{"file" => file ... })
    uuid = Video.uuid()
    tmp_path = Application.get_env(:myapp, :video_tmp_path) <> "/" <> uuid
    :ok = File.cp(file.path, tmp_path)
    VideoService.process(tmp_path, final_path)

VideoService 的内部如下所示。

defmodule MyApp.Services do
  defmodule VideoService do
    def process(tmp_path, final_path) do
      Task.start_link fn ->
        System.cmd("ffmpeg", ["-i", tmp_path, final_path, "-hide_banner"])
        File.rm(tmp_path)
      end
    end
  end
end

我在这里遇到的问题是,无论如何,System.cmd("ffmpeg") 调用之后的任何内容都不会在 VideoService 中执行,有时 System.cmd 调用甚至不会启动一个 ffmpeg 过程。我在这里做错了什么吗?我需要的是一种从控制器/服务在后台旋转此 ffmpeg shell 进程并在视频上传时响应 200 的方法。提前感谢您的帮助。我是 elixir / OTP 的新手,所以我确定我在做一些愚蠢的事情。

我也随机看到如下错误

erl_child_setup: failed with error 32 on line 240

感谢mudasobwa 和Dogbert 的帮助来回答这个问题。我跟着 this guide 找到了一个独立的主管,并像他们建议的那样 运行 在我的 phoenix stack 中加入。这样做的要点是在 lib/myapp.ex 中添加一行,创建一个这样的主管

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

然后当你想 运行 以那个主管为父的任务时,你可以 运行

Task.Supervisor.start_child(MyApp.TaskSupervisor, fn -> do_stuff() end)

在我的案例中,do_stuff 涉及生成一个 ffmpeg 进程并进行一些文件清理。这里的基本思想是,您正在以 Supervisor 作为父级进行这项工作,这样即使请求退出并且该进程终止,您的 supervisor 仍然活着并且很好,因为当您的应用程序启动时它始终处于运行状态。