Elixir Supervisor停止:bad_return错误

问题描述 投票:0回答:1

自从2周以来,我尝试对我们公司的Elixir应用程序进行全面重构,因为我们有太多的流程问题。

所以我从头开始,一步一步做好工作。而现在,因为差不多3天我面临同样的错误,: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

我究竟做错了什么 ?

elixir supervisor
1个回答
1
投票

我不认为这是正确的:

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

start:键的值告诉主管如何启动子StreamSubscriber,并告诉主管用StreamSubscriber参数调用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方法签名,

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

一旦你得到函数调用以匹配函数defs,你可以通过这样做来解决bad_return问题:

Module-based supervisors 在上面的示例中,通过将监督结构传递给start_link / 2来启动主管。但是,也可以通过明确定义监督模块来创建监督者:

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()。这是我在实现这些更改时得到的输出:

~/elixir_programs/app1$ iex -S mix
Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]
Interactive Elixir (1.6.6) - press Ctrl+C to exit (type h() ENTER for help)

iex(1)> MainSupervisor.start_link(1)

20:42:11.324 [info]  MainSupervisor => Starting...

20:42:11.324 [info]  MainSupervisor => Initializing...

20:42:11.327 [info]  AccountStreamSupervisor => Starting...

20:42:11.328 [info]  AccountStreamSupervisor => Initializing...

20:42:11.328 [debug] AccountStreamDispatcher => Starting...

20:42:11.328 [info]  AccountStreamDispatcher => Initializing...

20:42:11.328 [debug] StreamSubscriber => Starting... #(Elixir.StreamSubscriber)

20:42:11.329 [debug] StreamSubscriber => Initializing... (Elixir.StreamSubscriber)
{:ok, #PID<0.214.0>}

iex(2)>
© www.soinside.com 2019 - 2024. All rights reserved.