在 Elixir 中运行时从主管子项列表中添加/删除子项

发布于 2025-01-11 06:23:20 字数 828 浏览 1 评论 0原文

我知道如何创建一个带有初始儿童列表的主管。

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

但是我怎样才能实现在运行时添加新子项或从该列表中删除子项的功能呢?通过这种方式,其他参与者可以在运行时调用这些函数来添加或删除子项。

I know how to create a supervisor with an initial list of 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

But how can I make the functionality for adding new children or removing children from this list at runtime? In such way that other actors could call these functions while running in order to add or remove children.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

表情可笑 2025-01-18 06:23:20

一种可能的方法是使用 DynamicSupervisor,然后使用一次性工作人员为主管提供其初始子项。

假设这是您进程的裸骨架代码,

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

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

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

。发生的情况是,您的动态主管将在没有子级的情况下启动。

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

之后,您可以使用同一行代码来启动更多的子项。

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

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

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

A possible approach would be to use a DynamicSupervisor, followed by a one-off worker that gives the supervisor it's initial children.

Assuming this is the bare skeleton code for you processes

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

You can then add this to your application children

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

What happens is, your dynamic supervisor will start with no children.

Then the one off task will run, and start the 5 children you want it to start with.

After that, you can use the same line of code to start more children.

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

Of course, you can make it nicer by defining helper functions, but to get the base functionality going, this should do the trick.

Also note that, as others here have said, you should probably avoid atoms as ids when you're starting processes dynamically, as they don't get cleaned up fully and there's an overall limit on the system.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文