Elixir Supervisor停止并显示:bad_return错误

时间:2019-02-15 17:32:42

标签: elixir supervisor

自2周以来,我尝试对我们公司的Elixir应用程序进行完整的重构,因为我们遇到了太多的流程问题。

所以我从头开始,一步一步地工作。现在,由于将近三天的时间,当我在主管中上班时,我遇到了同样的错误:bad_return。我的进程树是这样:

Application |- MainSupervisor |- Some workers (Extreme for EventStore, Repo for PostgreSQL, and a stream subscriber for eventstore) |- AccountStreamSupervisor |- AccountStreamDispatcher (Supervisor) |- StreamSubscriber (Worker)

调度程序和订阅服务器具有start_child功能(因此稍后将在运行时使用)

我为每个主管使用Supervisor.start_link/2初始化我的树。应用程序,MainSupervisor,AccountStreamSupervisor都可以正常启动,但是在初始化AccountStreamDispatcher时,我遇到了这个:bad_return错误。

该跟踪信息表明AccountStreamDispatcher的init/1是问题所在,因为它返回{:ok, #PID<0.392.0>(根据文档,这是一个很好的响应)。

我尝试了很多事情,例如更改start_linkinit方法签名,更改子级声明,总是一样。我知道没有调度员,一切都会正常启动...

这是一些代码:

defmodule MainSupervisor do
  use Supervisor
  require Logger

  def start_link(_args) do
    Logger.info("MainSupervisor => Starting...")

    result = Supervisor.start_link(__MODULE__, name: :main_supervisor)

    case result do
      {:ok, _} ->
        nil

      :ignore ->
        Logger.error("Unable start main supervisor because is ignored")

      {:error, {:already_started, _}} ->
        Logger.error("Unable start main supervisor because is already started")

      {:error, {:shutdown, reason}} ->
        Logger.error("Unable start main supervisor because #{IO.inspect(reason)}")

      {:error, reason} ->
        Logger.error("Unable start main supervisor because #{IO.inspect(reason)}")
    end

    result
    end

    def init(_) do
    Logger.info("MainSupervisor => Initializing...")

    event_store_settings = Application.get_env(:extreme, :event_store)

    children = [
      [...]
      %{
        id: ViewBuilder.V2.AccountStreamSupervisor,
        start: {ViewBuilder.V2.AccountStreamSupervisor, :start_link, []},
        type: :supervisor
      }
    ]

    Supervisor.start_link(children, strategy: :one_for_one)
  end
end

defmodule AccountStreamSupervisor do
  use Supervisor
  require Logger

  def start_link do
    Logger.info("AccountStreamSupervisor => Starting...")

    result = Supervisor.start_link(__MODULE__, name: :account_supervisor)

    case result do
      {:ok, _} ->
        nil

      :ignore ->
        Logger.error("Unable start account stream supervisor because is ignored")

      {:error, {:already_started, _}} ->
        Logger.error("Unable start account stream supervisor because is already started")

      {:error, {:shutdown, reason}} ->
        Logger.error("Unable start account stream supervisor because #{IO.inspect(reason)}")

      {:error, reason} ->
        Logger.error("Unable start account stream supervisor because #{IO.inspect(reason)}")
    end

    result
  end

  def init(_) do
    Logger.info("AccountStreamSupervisor => Initializing...")

    children = [
  %{
    id: AccountStreamDispatcher,
    start: {AccountStreamDispatcher, :start_link, []},
    type: :supervisor
  }
    ]

    Supervisor.start_link(children, strategy: :one_for_one)
  end

  def start_child(account_stream_name) do
    Logger.debug(
      "AccountStreamSupervisor => Start a new child - AccountStreamDispatcher with the name: #{
        account_stream_name
      }"
    )

    Supervisor.start_child(:account_supervisor, [])
  end
end

defmodule AccountStreamDispatcher do
  use Supervisor
  require Logger

  def start_link do
    Logger.debug("AccountStreamDispatcher => Starting...")

    result = Supervisor.start_link(__MODULE__, name: :account_dispatcher)
    IO.inspect(result)
    case result do
      {:ok, _} ->
        nil

      :ignore ->
        Logger.error("Unable start dispatcher because is ignored")

      {:error, {:already_started, pid}} ->
        Logger.debug("Dispatcher is already started with pid #{pid}")

      {:error, reason} ->
        Logger.error("Unable start dispatcher because #{IO.inspect(reason)}")
    end

    result
  end

  def init(_) do
    Logger.info("AccountStreamDispatcher => Initializing...")

    children = [
      %{
        id: StreamSubscriber,
        start: {StreamSubscriber, :start_link, []},
        type: :supervisor
      }
    ]

    Supervisor.start_link(children, [strategy: :one_for_one])
  end

  def start_child(account_stream_name, type, account_id, sub_keys) do
    Logger.debug(
      "AccountStreamDispatcher => Start a new child - StreamSubscriber with the name: #{
        account_stream_name
      }"
    )

    Supervisor.start_child(
      :account_dispatcher,
      [
        %{
          stream_name: account_stream_name,
          stream_type: type,
          account_id: account_id,
          sub_keys: sub_keys
        }
      ]
    )
  end
end

defmodule StreamSubscriber do
  use GenServer
  require Logger

  alias EventHandler.EventHandlerProvider, as: EventHandlerProvider

   def start_link(
          args = %{
            stream_name: name,
            stream_type: _type,
            account_id: _account_id,
            sub_keys: _sub_keys
          }
      ) do
    Logger.debug("StreamSubscriber => Starting... (#{name})")

    result = GenServer.start_link(__MODULE__, args, name: name)

    case result do
      {:ok, _} ->
        nil

      :ignore ->
        Logger.error("Unable start process #{name} because is ignored")

      {:error, {:already_started, _}} ->
        Logger.error("Unable start process #{name} because is already started")

      {:error, reason} ->
        Logger.error("Unable start process #{name} because #{IO.inspect(reason)}")
    end

    result
  end

  def init(%{stream_name: name, stream_type: type, account_id: account_id, sub_keys: sub_keys}) do
    Logger.debug("StreamSubscriber => Initializing... (#{name})")

    state = %{stream_name: name, stream_type: type, account_id: account_id, sub_keys: sub_keys}

    {:ok, _} = EventHandlerProvider.create_handler(type, name, account_id, sub_keys)

    {:ok, state}
  end
end

我做错了什么?

1 个答案:

答案 0 :(得分:1)

我认为这是不正确的:

   children = [
      %{
        id: StreamSubscriber,
        start: {StreamSubscriber, :start_link, []},
        type: :supervisor
      }
    ]

start:键的值告诉Supervisor如何启动子项StreamSubscriber,并且您正在告诉Supervisor使用以下命令调用StreamSubscriber的{​​{1}}函数自变量start_link(),但您在[]中定义了start_link(),如下所示:

StreamSubscriber

但是 def start_link( args = %{ stream_name: name, stream_type: _type, account_id: _account_id, sub_keys: _sub_keys } ) do ... 无法与地图进行模式匹配。

  

我已经尝试了很多事情,例如更改start_link和init方法签名,

也许您在尝试解决问题后发布了一些错误的代码?

一旦获得与函数def匹配的函数调用,就可以通过以下方法解决[]问题:

  

Module-based supervisors
  在上面的示例中,通过将监管结构传递给start_link / 2来启动监管器。然而,   还可以通过显式定义监管来创建监管   模块:

bad_return
     

两种方法之间的区别在于,基于模块   主管可让您直接控制主管的行为   初始化。而不是使用列表调用defmodule MySupervisor do use Supervisor def start_link(init_arg) do Supervisor.start_link(__MODULE__, init_arg, name: __MODULE__) end @impl true def init(_init_arg) do children = [ {Stack, [:hello]} ] Supervisor.init(children, strategy: :one_for_one) end end   自动初始化的子代,我们手动   通过在其内部调用Supervisor.start_link/2来初始化子级   init / 1回调。

在所有主管的Supervisor.init/2方法中,您需要调用init()而不是Supervisor.init()。这是实现这些更改时得到的输出:

Supervisor.start_link()