主管不会在 econnrefused 上重新启动(在 init/1 中抛出)

Posted

技术标签:

【中文标题】主管不会在 econnrefused 上重新启动(在 init/1 中抛出)【英文标题】:Supervisor does not restart on econnrefused (thrown in init/1) 【发布时间】:2017-11-11 06:49:30 【问题描述】:

我有一个 gen_server 的 init 函数,我连接到 rabbitmq。当一切正常时,它工作得非常好,但是当连接到rabbitmq失败并且我调用exit时,进程不会重新启动。

我想让主管在我调用exit 后重新启动这个过程。

从概念上讲,我的init 函数是这样的:

init(_Args) ->
  process_flag(trap_exit, true),
  case connect() of
    error, econnrefused ->
            timer:sleep(1000),
            exit(econnrefused);
    ok, Connection ->
            .....
  end,
  ok, .

这是我的主管:

-module(tasks_manager_sup).
-behaviour(supervisor).
-export([start_link/0]).
-export([init/1]).
-define(SERVER, ?MODULE).

start_link() ->
    supervisor:start_link(local, ?SERVER, ?MODULE, []).

init([]) ->
    ok,
     #strategy => one_for_one, 
        intensity => 50,
        period => 10,
      [#id => tasks_manager_serv_id,
         start => tasks_manager_serv, start_link, [],
         restart => permanent,
         shutdown => brutal_kill,
         type => worker,
         modules => [tasks_manager_serv]].

我收到的错误如下。您可以看到此错误没有重新启动,它只是终止:

Starting global,tasks_da_serv (<0.479.0>)
Starting global,tasks_manager_serv (<0.483.0>)

 =INFO REPORT==== 9-Jun-2017::09:52:46 ===
     application: tasks
     exited: shutdown,
                  failed_to_start_child,tasks_manager_sup_id,
                      shutdown,
                          failed_to_start_child,tasks_manager_serv_id,
                              econnrefused,
              tasks_app,start,[normal,[]]
     type: permanent
 "Kernel pid terminated",application_controller,"application_start_failure,tasks,shutdown,failed_to_start_child,tasks_manager_sup_id,shutdown,failed_to_start_child,tasks_manager_serv_id,econnrefused,tasks_app,start,[normal,[]]"
 Kernel pid terminated (application_controller) (application_start_failure,tasks,shutdown,failed_to_start_child,tasks_manager_sup_id,shutdown,failed_to_start_child,tasks_manager_serv_id,econnrefu

我也尝试过向self()(来自init 函数)发送一条消息,并在handle_cast 中连接到rabbit,但效果不佳。

我仍在学习 Erlang/OTP,如果我问一些明显的问题,但我在文档中找不到任何问题的答案,请原谅我。

【问题讨论】:

我找不到这个文档,但看起来主管没有重新启动进程,因为它在 init 中退出。如果我添加self() ! connect 然后退出handle_info(connect, ...),它会为我重新启动它。 @Dogbert,supervisor:start_link() docs? ---> 如果supervisor及其子进程创建成功(即所有子进程启动函数返回ok,Child, ok,Child,Info, 或者忽略),函数返回 ok,Pid,其中 Pid 是主管的 pid....如果 Module:init/1 失败或返回不正确的值,则此函数返回 error,Term,其中 Term 是包含有关错误信息的术语,并且主管因Term终止 换句话说,你必须能够让主管的孩子第一次启动——否则不会创建主管进程。 @7stud 不是在OP问题中开始的supervisor,而是由它开始的gen_server 主管子启动确实必须成功,但没有什么可以阻止您设计init/1 尝试多次连接,然后最终放弃。如果主管启动其他希望rabbitmq 设施可用的孩子,那么这可能比允许它启动并异步尝试连接要好......但你真的需要重试吗?如果您在不重试的情况下进行连接,则整个应用程序会由于外部资源不可用而相对较快地失败,并且管理员很清楚可以处理它。 【参考方案1】:

感谢问题下方的 cmets,我能够解决它。 基本上问题是进程没有正确启动,因为exit(econnrefused)init/1 函数中。这就是主管没有重新启动进程的原因 - 它不会重新启动未能初始化的进程。

现在我正在向self() 发送一条消息,然后在hangle_info/2 中捕获它,如下所示:

init(_Args) ->
    process_flag(trap_exit, true),
    io:format("Starting ~p (~p)~n", [global, ?MODULE, self()]),
    self() ! connect,
    ok, .

handle_info(connect, State) ->
    ok, Connection, Channel = establish_rabbit_connection(),
    noreply, #stateconnection = Connection, channel = Channel;

【讨论】:

以上是关于主管不会在 econnrefused 上重新启动(在 init/1 中抛出)的主要内容,如果未能解决你的问题,请参考以下文章

Laravel 水平管理器在水平:: 终止并运行伪造守护程序后不会重新启动

如何让主管重新启动挂起的工人?

重新启动 => erlang 主管的瞬态与永久

主管的所有孩子都死后会发生啥?

Elixir - 基本主管设置崩溃而不是重新启动子进程

我怎么知道这是由 erlang 中的主管重新启动我的进程的最后一个周期