在 OTP 中启动主管的童工时如何传递额外的参数?

How to pass in extra arguments when starting a child worker of a Supervisor in OTP?

假设我有以下设置:

defmodule NestedSupervisorTree do
  # this will be the top line supervisor
  use Supervisor

  def start_link, do: Supervisor.start_link(__MODULE__, :ok, name: __MODULE__)

  def init(:ok) do
    children = [
      supervisor(BranchSupervisor, [], restart: :temporary)
      #worker(TreeWorker, [], restart: :temporary)
    ]

    supervise(children, strategy: :simple_one_for_one)

  end

  def start_branch(args) do
    {_, branch_id} = Supervisor.start_child(__MODULE__, [args])
  end
end

defmodule BranchSupervisor do
  # this will be the top line supervisor
  use Supervisor

  def start_link(args), do: Supervisor.start_link(__MODULE__, [args], name: __MODULE__)

  def init(args) do
    IO.puts "branch init args:"
    IO.inspect args
    children = [
      worker(TreeWorker, [args], restart: :temporary)
    ]

    supervise(children, strategy: :simple_one_for_one)
  end

  def start_worker do
    {_, wid} = Supervisor.start_child(__MODULE__, [3])
  end
end

defmodule TreeWorker do
  def start_link(args) do
    IO.puts "worker args:"
    IO.inspect args
    #IO.puts String.codepoints raw
    {:ok, spawn(fn -> loop end)}
  end

  def loop do
    receive do
      :stop -> :ok

      msg ->
        IO.inspect msg
        loop
    end
  end
end

假设我在 iex 终端中按以下顺序发出以下命令:

 iex> {_, pid} = NestedSupervisorTree.start_link
 iex> {_, cid} = NestedSupervisorTree.start_branch(2)
 iex> BranchSupervisor.start_worker
 # returns:
{:error,
 {:EXIT,
  {:undef,
   [{TreeWorker, :start_link, [[2], 3], []},
    {:supervisor, :do_start_child_i, 3, [file: 'supervisor.erl', line: 359]},
    {:supervisor, :handle_call, 3, [file: 'supervisor.erl', line: 384]},
    {:gen_server, :try_handle_call, 4, [file: 'gen_server.erl', line: 629]},
    {:gen_server, :handle_msg, 5, [file: 'gen_server.erl', line: 661]},
    {:proc_lib, :init_p_do_apply, 3, [file: 'proc_lib.erl', line: 240]}]}}}

如何在对 BranchSupervisor.start_worker 的调用中注入 "extra arguments"?如果我删除 {_, wid} = Supervisor.start_child(__MODULE__, [3]) 中的“3”,一切正常。这甚至是正确的做法吗?

这主要是一个学习练习。

这里的[3]被附加到BranchSupervisor.init/1中传递给workerargs,也就是[[2]],使得最后的参数[[2]] ++ [3] => [[2], 3]。由于列表是两个元素长,Supervisor 然后调用 TreeWorker.start_link/2 就像 TreeWorker.start_link([2], 3),所以如果你想像这样接受两个参数,你只需要更改 start_link 以接受两个参数:

defmodule TreeWorker do
  def start_link(arg1, arg2) do
    # `arg1 == [2]` and `arg2 == 3` here
    ...
  end
end

此行为记录在 Supervisor.start_child/2:

In the case of :simple_one_for_one, the child specification defined in the supervisor is used and instead of a child_spec, an arbitrary list of terms is expected. The child process will then be started by appending the given list to the existing function arguments in the child specification.