如何将参数传递给 :simple_one_for_one GenServer 以初始化状态

How to pass arguments to :simple_one_for_one GenServer to initialize state

我正在尝试为调度程序应用程序设置监督树(注意使用 Elixir 1.5 语法)。应用程序 应该 工作以便:

如果我 传递任何参数,我可以使它正常工作 - 创建的计划未注册,我必须在创建后修改状态。一旦我尝试添加参数,系统就会出错——我知道这只是我的语法误解,但我终究无法弄清楚我做错了什么。我没有发现这里的文档非常有用,我已经尝试从 GH、GH Gists 和在线文章中复制和修改示例,但我无法让它工作。

当前设置 - 理想情况下,我想将 idperiodtargets 作为参数传递给 start_child,但甚至不能让它与一个参数,所以坚持使用那个直到我能得到它 运行:

申请:

defmodule Assay.Application do
  use Application

  def start(_type, _args) do
    children = [
      {Assay.SchedulerSupervisor, []},
      {Registry, keys: :unique, name: Assay.Scheduler.Registry}
    ]

    opts = [strategy: :one_for_all, name: Assay.Supervisor]
    Supervisor.start_link(children, opts)
  end
end

主管:

defmodule Assay.SchedulerSupervisor do
    use Supervisor

    @name Assay.SchedulerSupervisor

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

    def start_schedule(id) do
        Supervisor.start_child(@name, [id])
    end

    def init(_) do
        Supervisor.init([Assay.Scheduler], [strategy: :simple_one_for_one, name: @name])
    end
end

GenServer(仅显示相关的初始化函数)

defmodule Assay.Scheduler do
    use GenServer
    alias Assay.Scheduler
    require Logger

    defstruct targets: [], period: 60_000, id: nil, active: false

    def start_link(id) do
        GenServer.start_link(__MODULE__, [id], [name: via_tuple(id)])
    end

    def init([id]) do
        Logger.info "starting a new #{__MODULE__} with id #{id}"
        {:ok, %Scheduler{id: id}}
    end
end

编辑:实际错误可能会有所帮助 - 我可以看到参数是错误的,我就是不明白为什么:

{:error,
 {:EXIT,
  {:undef,
   [{Assay.Scheduler, :start_link, [[], 1], []},
    {:supervisor, :do_start_child_i, 3, [file: 'supervisor.erl', line: 381]},
    {:supervisor, :handle_call, 3, [file: 'supervisor.erl', line: 406]},
    {:gen_server, :try_handle_call, 4, [file: 'gen_server.erl', line: 636]},
    {:gen_server, :handle_msg, 6, [file: 'gen_server.erl', line: 665]},
    {:proc_lib, :init_p_do_apply, 3, [file: 'proc_lib.erl', line: 247]}]}}}

对于 :simple_one_for_one 监督者,Supervisor.start_child 使用子规范中指定的参数调用启动函数。当使用 Supervisor.init 时,子规范取自 Elixir 1.5 中模块的 child_spec/1 函数。由于您正在使用 GenServer 并且未指定自定义启动函数并且 [] 被传递给 child_spec/1,因此此 defaults to [[]] 这意味着您的函数最终是如果 id1 并且出现未定义函数错误,则使用两个参数 []1 调用。

您可以通过明确说明您不希望 GenServer 通过更改

child_spec 中的启动函数提供任何参数来解决此问题
use GenServer

use GenServer, start: {__MODULE__, :start_link, []}

现在函数将被正确调用,只有一个参数,即 id.

IO.inspect Assay.SchedulerSupervisor.start_link []
IO.inspect Assay.SchedulerSupervisor.start_schedule 12

将打印:

{:ok, #PID<0.82.0>}
[info]  starting a new Elixir.Assay.Scheduler with id 12
{:ok, #PID<0.83.0>}