Add/remove child 来自 supervisor children list 在 Elixir 运行时

Add/remove child from supervisor children list at runtime in Elixir

我知道如何创建一个初始列表为 children 的主管。

defmodule TweetProcesser.DummySupervisor do
  use Supervisor

  def start_link(opts) do
    Supervisor.start_link(__MODULE__, :ok, opts)
  end

  @impl true
  def init(:ok) do
    children = [
      Supervisor.child_spec({TweetProcesser.Worker, []}, id: :my_worker_1),
      Supervisor.child_spec({TweetProcesser.Worker, []}, id: :my_worker_2),
      Supervisor.child_spec({TweetProcesser.Worker, []}, id: :my_worker_3),
      Supervisor.child_spec({TweetProcesser.Worker, []}, id: :my_worker_4),
      Supervisor.child_spec({TweetProcesser.Worker, []}, id: :my_worker_5)
    ]

    opts = [strategy: :one_for_one, name: TweetProcesser.WorkerSupervisor]

    Supervisor.init(children, opts)
  end
end

但是我如何实现在运行时从该列表中添加新 children 或删除 children 的功能?以这种方式,其他参与者可以在 运行 时调用这些函数以添加或删除 children.

一种可能的方法是使用 DynamicSupervisor,然后是 one-off 工作人员,它为主管提供初始 children。

假设这是您进程的基本框架代码

defmodule TweetProcesser.Worker do
  use GenServer

  def start_link() do
    GenServer.start_link(__MODULE__, [], [])
  end

  def init(_) do
    {:ok, nil}
  end
end

defmodule StartInitialChildren do
  use Task

  def start_link([]) do
    Task.start_link(fn ->
      [:my_worker_1, :my_worker_2, :my_worker_3, :my_worker_4, :my_worker_5]
      |> Enum.each(fn id ->
        spec = %{id: id, start: {TweetProcesser.Worker, :start_link, []}}
        {:ok, _pid} = DynamicSupervisor.start_child(TweetProcesser.DummySupervisor, spec)
      end)
    end)
  end
end

然后您可以将其添加到您的应用程序中children

defmodule MyApp.Application do
  # ...
  def start(_, _) do
    def children = [
      # ...
      {DynamicSupervisor, name: TweetProcesser.DummySupervisor, strategy: :one_for_one},
      StartInitialChildren
      # ...
    ]
  end
  #...
end

实际情况是,您的动态主管将从 children 开始。

然后一次性任务将 运行,并启动您希望它开始的 5 children。

之后,您可以使用同一行代码开始更多children。

spec = %{id: some_id, start: {TweetProcesser.Worker, :start_link, []}}
DynamicSupervisor.start_child(TweetProcesser.DummySupervisor, spec)

当然,您可以通过定义辅助函数让它变得更好,但是为了让基本功能正常运行,这应该可以解决问题。

另请注意,正如此处其他人所说,当您动态启动进程时,您可能应该避免使用原子作为 id,因为它们不会被完全清理并且系统存在总体限制。