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

时间:2017-09-11 07:40:41

标签: erlang elixir gen-server

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

  • 应用程序启动时带有注册表&调度程序主管
  • SchedulerSupervisor启动,并允许通过start_child回调动态添加子项。这将采用用于构建调度状态的初始化args。
  • 调度程序在初始化时将其名称注册到注册表,并使用保存状态的结构进行初始化(调度程序然后具有操作调度的功能,但这与问题的相关性并不重要)具有)。

如果我没有传递任何参数,我可以使其工作 - 创建的计划表未注册,我必须在创建后修改状态。一旦我尝试添加参数,系统就会出错 - 我知道这只是我的语法误解,但我不能为我的生活弄清楚我做错了什么。我还没有发现这里的文档非常有用,我尝试过复制和修改来自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

编辑:实际错误可能会有所帮助 - 我可以看到args是错误的,我无法弄清楚原因:

{: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]}]}}}

1 个答案:

答案 0 :(得分:4)

对于:simple_one_for_one主管,Supervisor.start_child使用子规范中指定的参数调用start函数,并使用赋予它的参数。使用Supervisor.init时,子规范取自Elixir 1.5中模块的child_spec/1函数。由于您使用GenServer而没有指定自定义启动功能而[]已传递给child_spec/1,因此defaults to [[]]表示您的功能已结束如果[]1并且您得到未定义的函数错误,则使用两个参数id1进行调用。

您可以通过明确表示您不希望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>}