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,因为它们不会被完全清理并且系统存在总体限制。
我知道如何创建一个初始列表为 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,因为它们不会被完全清理并且系统存在总体限制。