简体   繁体   English

了解 Elixir badarg 错误消息

[英]Understanding Elixir badarg error message

When trying to start my process from a DynamicSupervisor im getting the following error:当尝试从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]}
   ]}}}

The code im using is:我使用的代码是:

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

However it is not clear to me what is going wrong.但是,我不清楚出了什么问题。 What argument to which function is it thats not correct?哪个函数的哪个参数不正确? How do I break down and read the given error message?如何分解并阅读给定的错误消息?

Update:更新:

This is the init method of my supervisor:这是我的主管的 init 方法:

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

Update 2: This is the start_link of market_service:更新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

Im using the default child_spec im getting from GenServer即时通讯使用默认child_spec IM从得到GenServer

Update 3: Changing to:更新 3:更改为:

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

Gives:给出:

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

Changing to:更改为:

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

Gives:给出:

** (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

You got badarg exception to function erlang:apply/3 when there are three arguments BfgEngine.MarketService , :start_link and {{BfgEngine.MarketService, :start_link, ["1111"]}, :permanent, 5000, :worker, [BfgEngine.MarketService]} and it happen in function supervisor:do_start_child_i/3 .你有badarg例外功能erlang:apply/3时,有三个参数BfgEngine.MarketService:start_link{{BfgEngine.MarketService, :start_link, ["1111"]}, :permanent, 5000, :worker, [BfgEngine.MarketService]}并且它发生在函数supervisor:do_start_child_i/3

The arguments to the function erlang:apply/3 should be MFA aka Module, Function, Arguments.函数erlang:apply/3应该是 MFA aka Module, Function, Arguments。 {{BfgEngine.MarketService, :start_link, ["1111"]}, :permanent, 5000, :worker, [BfgEngine.MarketService]} is not Arguments because it obviously is not a list of arguments . {{BfgEngine.MarketService, :start_link, ["1111"]}, :permanent, 5000, :worker, [BfgEngine.MarketService]}不是参数,因为它显然不是参数列表 From your code, I can guess the error is the content of variable spec .从您的代码中,我可以猜测错误是变量spec的内容。 You should provide some proplist or map.您应该提供一些 proplist 或地图。 I don't know, you should read the documentation of DynamicSupervisor more carefully.我不知道,你应该仔细阅读DynamicSupervisor的文档。

The error message错误信息

As of understanding the error message thrown by Elixir, you can refer to the official Erlang documentation .关于 Elixir 抛出的错误信息的理解,可以参考Erlang 官方文档 The section about errors and exceptions from Learn You Some Erlang For Great Good can help. Learn You Some Erlang For Great Good 中关于错误和异常的部分可以提供帮助。 The answer of @Hynek -Pichi Vychodil is also accurate. @Hynek -Pichi Vychodil的回答也很准确。

Your specific problem您的具体问题

As @Milan Jaric mentioned, your error would come from:正如@Milan Jaric提到的,您的错误将来自:

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

But not only!但不仅如此! DynamicSupervisor.start_child(__MODULE__, spec) is calling MarketService.start_link/1 ! DynamicSupervisor.start_child(__MODULE__, spec)正在调用MarketService.start_link/1 Your problem lies in the combination of this function from the DynamicSupervisor module, and the way you parse values in 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

Indeed, this code should work, if you also have implemented MarketService.init/1 correctly.事实上,如果您还正确实现了 MarketService.init/1,此代码应该可以工作。 I am not able to reproduce the error.我无法重现该错误。 Are you sure market_id is really a bitstring?你确定market_id真的是一个位串吗?

Personally, I had based my code on the 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

As you can see, they suggest to use a Keyword list here:如您所见,他们建议在此处使用关键字列表:

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

It works only if you've implemented MyWorker.start_link/1 as follows:仅当您按如下方式实现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}

In your case, if you change start_market/1 to:在您的情况下,如果您将 start_market/1 更改为:

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

It will not work because this MarketService.start_link/1 will fail:它不会工作,因为这个 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 here is not a bitstring but a Keyword list.这里的market_id 不是位bitstring而是关键字列表。 So you got to modify MarketService.start_link/1 function to:因此,您必须将 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

And write a MarketService.init/1 as follows:并写一个 MarketService.init/1 如下:

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

Helpful resources有用的资源

It is hard to tell from posted code, but could you try changing start_market to:从发布的代码中很难判断,但是您可以尝试将start_market更改为:

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

UPDATE (Below are two options):更新(以下是两个选项):

  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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM