【问题标题】:Understanding Elixir badarg error message了解 Elixir badarg 错误消息
【发布时间】:2018-12-16 21:45:57
【问题描述】:

当尝试从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

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

更新:

这是我主管的init方法:

  @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

【问题讨论】:

  • 你能分享一下你的主管的init函数的定义吗?
  • 问题出在您的MarketServicestart_link 代码中。要看的东西是你的 start_link 函数以及你的 child_spec。您可以将这些添加到您的帖子中吗?

标签: erlang elixir


【解决方案1】:

很难从发布的代码中看出,但您可以尝试将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

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

【讨论】:

  • 我已经更新了一些示例,但现在出现了不同的错误。
  • 你没有使用我的代码。它应该只是元组{MarketService, :market_id, market_id} 而不是{MarketService, market_id: market_id} 这是两个不同的数据结构。请注意 :market_id atom 后面的逗号!
  • 我知道这很旧,但是,我更新了答案。现在应该是正确的
【解决方案2】:

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

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

【讨论】:

    【解决方案3】:

    错误信息

    了解Elixir抛出的错误信息,可以参考the official Erlang documentation。 Learn You Some 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
    

    有用的资源

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-02
      相关资源
      最近更新 更多