Elixir Supervisor 因 :bad_return 错误而停止
Elixir Supervisor stops with :bad_return error
两周以来,我尝试对我们公司的 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)
dispatcher和subscriber有start_child
函数(所以后面会在运行时用到)
我用 Supervisor.start_link/2
为每个主管初始化我的树。应用程序、MainSupervisor、AccountStreamSupervisor 启动时没有问题,但是当涉及到要初始化的 AccountStreamDispatcher 时,我有这个 :bad_return
错误。
跟踪表明 AccountStreamDispatcher 的 init/1
是问题所在,因为它 return {:ok, #PID<0.392.0>
(根据文档,这是一个很好的响应)。
我尝试了很多东西,比如更改 start_link
和 init
方法签名,更改子声明,总是一样。我知道没有我的调度员,一切都正常启动...
这是一些代码:
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
我做错了什么?
我认为这是不正确的:
children = [
%{
id: StreamSubscriber,
start: {StreamSubscriber, :start_link, []},
type: :supervisor
}
]
start:
键的值告诉Supervisor如何启动子StreamSubscriber
,而你是在告诉Supervisor调用StreamSubscriber
的start_link()
函数使用参数 []
,但您在 StreamSubscriber
中定义了 start_link()
,如下所示:
def start_link(
args = %{
stream_name: name,
stream_type: _type,
account_id: _account_id,
sub_keys: _sub_keys
}
) do ...
但是[]
不能模式匹配地图。
I've tried so many things, like change start_link and init method signatures,
也许您在尝试解决问题后发布了一些错误代码?
一旦获得与函数 defs 匹配的函数调用,就可以通过执行以下操作来解决 bad_return
问题:
Module-based supervisors
In the example above, a supervisor was started by passing the supervision structure to start_link/2. However,
supervisors can also be created by explicitly defining a supervision
module:
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
The difference between the two approaches is that a module-based
supervisor gives you more direct control over how the supervisor is
initialized. Instead of calling Supervisor.start_link/2
with a list
of children that are automatically initialized, we manually
initialized the children by calling Supervisor.init/2
inside its
init/1 callback.
在你主管的所有 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)>
两周以来,我尝试对我们公司的 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)
dispatcher和subscriber有start_child
函数(所以后面会在运行时用到)
我用 Supervisor.start_link/2
为每个主管初始化我的树。应用程序、MainSupervisor、AccountStreamSupervisor 启动时没有问题,但是当涉及到要初始化的 AccountStreamDispatcher 时,我有这个 :bad_return
错误。
跟踪表明 AccountStreamDispatcher 的 init/1
是问题所在,因为它 return {:ok, #PID<0.392.0>
(根据文档,这是一个很好的响应)。
我尝试了很多东西,比如更改 start_link
和 init
方法签名,更改子声明,总是一样。我知道没有我的调度员,一切都正常启动...
这是一些代码:
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
我做错了什么?
我认为这是不正确的:
children = [
%{
id: StreamSubscriber,
start: {StreamSubscriber, :start_link, []},
type: :supervisor
}
]
start:
键的值告诉Supervisor如何启动子StreamSubscriber
,而你是在告诉Supervisor调用StreamSubscriber
的start_link()
函数使用参数 []
,但您在 StreamSubscriber
中定义了 start_link()
,如下所示:
def start_link(
args = %{
stream_name: name,
stream_type: _type,
account_id: _account_id,
sub_keys: _sub_keys
}
) do ...
但是[]
不能模式匹配地图。
I've tried so many things, like change start_link and init method signatures,
也许您在尝试解决问题后发布了一些错误代码?
一旦获得与函数 defs 匹配的函数调用,就可以通过执行以下操作来解决 bad_return
问题:
Module-based supervisors
In the example above, a supervisor was started by passing the supervision structure to start_link/2. However, supervisors can also be created by explicitly defining a supervision module: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
The difference between the two approaches is that a module-based supervisor gives you more direct control over how the supervisor is initialized. Instead of calling
Supervisor.start_link/2
with a list of children that are automatically initialized, we manually initialized the children by callingSupervisor.init/2
inside its init/1 callback.
在你主管的所有 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)>