了解Elixir Badarg错误消息

时间:2018-07-09 08:57:51

标签: erlang elixir

当尝试从DynamicSupervisor我的进程开始时,出现以下错误:

{:error,
 {:EXIT,
  {:badarg,
   [
     {:erlang, :apply,
      [
        BfgEngine.MarketService,
        :start_link,
        {{BfgEngine.MarketService, :start_link, ["1111"]}, :permanent, 5000,
         :worker, [BfgEngine.MarketService]}
      ], []},
     {:supervisor, :do_start_child_i, 3, [file: 'supervisor.erl', line: 379]},
     {:supervisor, :handle_call, 3, [file: 'supervisor.erl', line: 404]},
     {:gen_server, :try_handle_call, 4, [file: 'gen_server.erl', line: 661]},
     {:gen_server, :handle_msg, 6, [file: 'gen_server.erl', line: 690]},
     {:proc_lib, :init_p_do_apply, 3, [file: 'proc_lib.erl', line: 249]}
   ]}}}

我使用的代码是:

  def start_market(market_id) do
    spec = {MarketService, market_id}
    DynamicSupervisor.start_child(__MODULE__, spec)
  end

但是我不清楚发生了什么问题。哪个函数对哪个参数不正确?如何分解并阅读给定的错误消息?

更新:

这是我的主管的初始化方法:

  @impl true
  def init(initial_arg) do
    DynamicSupervisor.init(
      strategy: :one_for_one,
      extra_arguments: [initial_arg]
    )
  end

更新2: 这是market_service的start_link:

  def start_link(market_id) when is_bitstring(market_id) do
    GenServer.start_link(__MODULE__, market_id, name: via_tuple(market_id))
  end

我使用默认的child_spec即时通讯从GenServer获取

更新3: 更改为:

  def start_market(market_id) do
    spec = {MarketService, market_id: market_id}
    DynamicSupervisor.start_child(__MODULE__, spec)
  end

礼物:

{:error,
 {:undef,
  [
    {BfgEngine.MarketService, :start_link, [[], [market_id: "222"]], []},
    {DynamicSupervisor, :start_child, 3,
     [file: 'lib/dynamic_supervisor.ex', line: 654]},
    {DynamicSupervisor, :handle_start_child, 2,
     [file: 'lib/dynamic_supervisor.ex', line: 640]},
    {:gen_server, :try_handle_call, 4, [file: 'gen_server.erl', line: 661]},
    {:gen_server, :handle_msg, 6, [file: 'gen_server.erl', line: 690]},
    {:proc_lib, :init_p_do_apply, 3, [file: 'proc_lib.erl', line: 249]}
  ]}}

更改为:

  def start_market(market_id) do
    spec = {MarketService, :market_id, market_id}
    DynamicSupervisor.start_child(__MODULE__, spec)
  end

礼物:

** (ArgumentError) supervisors expect each child to be one of:

  * a module
  * a {module, arg} tuple
  * a child specification as a map with at least the :id and :start fields
  * or a tuple with 6 elements generated by Supervisor.Spec (deprecated)

Got: {BfgEngine.MarketService, :market_id, "222"}

    (elixir) lib/supervisor.ex:657: Supervisor.init_child/1
    (elixir) lib/supervisor.ex:744: Supervisor.child_spec/2
    (elixir) lib/dynamic_supervisor.ex:304: DynamicSupervisor.start_child/2

3 个答案:

答案 0 :(得分:1)

当存在三个参数badargerlang:apply/3BfgEngine.MarketService时,函数:start_link出现{{BfgEngine.MarketService, :start_link, ["1111"]}, :permanent, 5000, :worker, [BfgEngine.MarketService]}异常,并且它在函数supervisor:do_start_child_i/3中发生

函数erlang:apply/3的参数应该是MFA,也就是模块,函数,参数。 {{BfgEngine.MarketService, :start_link, ["1111"]}, :permanent, 5000, :worker, [BfgEngine.MarketService]}不是自变量,因为它显然不是自变量列表。从您的代码中,我可以猜到错误是变量spec的内容。您应该提供一些属性表或地图。我不知道,您应该更仔细地阅读DynamicSupervisor的文档。

答案 1 :(得分:1)

错误消息

从了解Elixir引发的错误消息开始,您可以参考the official Erlang documentation。可以学到一些Erlang for Great Good中有关errors and exceptions的部分可以提供帮助。 @Hynek -Pichi Vychodil的答案也很准确。

您的具体问题

@Milan Jaric所述,您的错误将来自:

  def start_market(market_id) do
    spec = {MarketService, market_id}
    DynamicSupervisor.start_child(__MODULE__, spec)
  end

但不仅如此! DynamicSupervisor.start_child(__MODULE__, spec)正在呼叫MarketService.start_link/1! 您的问题在于DynamicSupervisor模块中此功能的组合,以及在MarketService.start_link / 1中解析值的方式:

  def start_link(market_id) when is_bitstring(market_id) do
    GenServer.start_link(__MODULE__, market_id, name: via_tuple(market_id))
  end

实际上,如果您还正确实现了MarketService.init / 1,则此代码应该有效。我无法重现该错误。您确定market_id确实是位串吗?

我个人是基于official documentation编写代码的:

defmodule MySupervisor do
  use DynamicSupervisor

  def start_link(init_arg) do
    DynamicSupervisor.start_link(__MODULE__, init_arg, name: __MODULE__)
  end

  def start_child(foo, bar, baz) do
    # If MyWorker is not using the new child specs, we need to pass a map:
    # spec = %{id: MyWorker, start: {MyWorker, :start_link, [foo, bar, baz]}}
    spec = {MyWorker, foo: foo, bar: bar, baz: baz}
    DynamicSupervisor.start_child(__MODULE__, spec)
  end

  @impl true
  def init(init_arg) do
    DynamicSupervisor.init(
      strategy: :one_for_one,
      extra_arguments: [init_arg]
    )
  end
end

如您所见,他们建议在此处使用关键字列表:

spec = {MyWorker, foo: foo, bar: bar, baz: baz}

仅当您按照以下方式实施MyWorker.start_link/1时,此方法才有效:

def start_link(args) do
   foo = Keyword.fetch!(args, :foo)
   bar = Keyword.fetch!(args, :bar)
   baz = Keyword.fetch!(args, :baz)
   Genserver.start_link(__MODULE__, {foo, bar, baz}, [])

def init({foo, bar, baz}) do
   # do something...
   state = {foo, bar, baz}
   {:ok, state}

如果您将start_market / 1更改为:

  def start_market(market_id) do
      spec = {MarketService, market_id: market_id}
      DynamicSupervisor.start_child(__MODULE__, spec)
  end

它将不起作用,因为此MarketService.start_link / 1将失败:

 def start_link(market_id) when is_bitstring(market_id) do
    GenServer.start_link(__MODULE__, market_id, name: via_tuple(market_id))
  end

market_id此处不是bitstring,而是关键字列表。因此,您必须将MarketService.start_link / 1函数修改为:

  def start_link(args) when is_list(args) do
    market_id = Keyword.fetch!(args, :market_id)
    GenServer.start_link(__MODULE__, market_id, name: via_tuple(market_id))
  end

并按如下所示编写MarketService.init / 1:

  def init(market_id) do
     # do something... Let's keep it simple for the example:
     state = market_id
     {:ok, state}
  end

有用的资源

答案 2 :(得分:0)

很难从发布的代码中看出来,但是您可以尝试将start_market更改为:

  def start_market(market_id) do
    spec = {MarketService, :market_id, market_id}
    DynamicSupervisor.start_child(__MODULE__, spec)
  end

更新(以下是两个选项):

  def start_market(market_id) do
    spec = &{
      id: MarketService,
      start: {MarketService, start_link, [market_id]},
      type: :worker
    }
    DynamicSupervisor.start_child(__MODULE__, spec)
  end

OR

  def start_market(market_id) do
    spec = {MarketService, [market_id]}
    DynamicSupervisor.start_child(__MODULE__, spec)
  end