【问题标题】:Strange error message when stopping app using lager and poolboy使用 lager 和 poolboy 停止应用程序时出现奇怪的错误消息
【发布时间】:2013-10-21 16:56:27
【问题描述】:

我使用 poolboy 和几乎空的 worker 创建了简单的应用程序,但是当我停止应用程序时,我看到 lager 打印以下错误:

10:50:26.363 [error] Supervisor {<0.236.0>,poolboy_sup} had child test_worker started with test_worker:start_link([]) at undefined exit with reason shutdown in context shutdown_error

是什么导致了这个错误,我该如何解决这个问题?

主管:

-module(test_sup).
-behaviour(supervisor).
-export([start_link/0, init/1]).


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

init([]) ->
    ChildSpecs = [pool_spec()],
    {ok, {{one_for_one, 1000, 3600}, ChildSpecs}}.

pool_spec() ->
    Name = test_pool,
    PoolArgs = [{name, {local, Name}},
                {worker_module, test_worker},
                {size, 10},
                {max_overflow, 20}],
    poolboy:child_spec(Name, PoolArgs, []).

工人:

-module(test_worker).
-behaviour(gen_server).
-behaviour(poolboy_worker).

-export([start_link/1]).
-export([init/1, handle_call/3, handle_cast/2,
     handle_info/2, terminate/2, code_change/3]).

-record(state, {}).

start_link([]) ->
    gen_server:start_link(?MODULE, [], []).

init([]) ->
    {ok, #state{}}.

handle_call(_Request, _From, State) ->
    {reply, _Reply = ok, State}.

handle_cast(_Msg, State) ->
    {noreply, State}.

handle_info(_Info, State) ->
    {noreply, State}.

terminate(_Reason, _State) ->
    ok.

code_change(_OldVsn, State, _Extra) ->
    {ok, State}.

应用程序的其余部分非常标准。

二郎:R16B02

台球男孩:1.0.1

Lager:在撰写问题时来自大师的最新版本 (822062478a223313dce30e5a45e30a50a4b7dc4e)

【问题讨论】:

  • 我从未听说过 poolboy,但从他们网站上的示例来看,您似乎需要在 worker 中实现 poolboy_worker 行为,不是吗?
  • 我试过 -behaviour(poolboy_worker) - 没有帮助。更新了帖子中的源代码。
  • 我也遇到了这个问题;看起来它还没有在 Poolboy 中修复,而且 Paul 删除链接的建议让我有点恼火......
  • 作为一个小更新:建议的修复 github.com/devinus/poolboy/pull/37#issuecomment-27363909 在这里有效。

标签: erlang


【解决方案1】:

您看到的错误实际上不是错误,而是lager 生成的错误报告。此报告似乎是由poolboy 中的一个错误引起的。

您可以:

  • 修复错误并向 poolboy 开发人员提交补丁。
  • 安全地忽略该报告。
  • 在退出时手动终止您的工作人员。

当您停止 OTP 应用程序时应该发生的是监督树用于终止所有进程,最好是优雅地终止。默认的做法是向受监督的进程发送shutdown 信号,如果一段时间后这不起作用,则残酷地杀死它们。当一切顺利时,您永远不会收到任何报告。

有两个 Erlang 微妙之处可以理解这个 bug:

  1. 进程可以是linked,这意味着当一个进程异常终止时(即由于normal以外的原因),所有链接的进程都以相同的原因终止。此原语是 OTP 监督的基础。
  2. 一个进程可以trap exit signals(或trap exits),这意味着它作为常规消息接收退出信号而不是被终止(包括normal,它不会终止它,但不包括@ 987654335@ 将无条件终止它)。

与捕获出口结合的链接通常用于监控进程的终止,另外还有一个好处是在监控进程终止时终止受监控的进程。例如,如果主管终止,则其子女也应终止。还存在不对称的monitor 机制。

在这里,您的主管(实施 test_sup 行为)以 shutdown 的原因终止,应该是这样。主管行为实际上会捕获出口,当它收到shutdown 信号时,它会尝试根据其关闭策略终止其子项。在这里,您使用默认策略,即向孩子发送shutdown 信号作为第一次尝试。因此,您的主管将 shutdown 信号发送给其唯一的孩子。

Poolboy 在这里介绍它的魔力,你的主管的孩子实际上是一个带有poolboy 回调模块的gen_server。它应该关闭池并正常终止。

这个模块链接到pool supervisor,也链接到the workers。这个令人惊讶的实现选择可能是池的崩溃(poolboy gen_server)将终止工人。然而,这是错误的根源,非对称监视器可能更有意义。由于主管已经链接到 poolboy gen_server,因此终止 poolboy 进程最终将导致工作人员终止。

链接到工作人员的结果是他们还获得了shutdown 退出信号,该信号最初被定向到poolboy 进程。他们被终止了。这个终止被工人的主管(实现poolboy_sup回调)认为是异常的,因为它本身没有发送信号。结果,主管报告了关闭,这里由 lager 记录。

poolboytraps exits 这一事实并不能阻止shutdown 信号的传播。进程在接收到信号时不会立即终止,而是将其作为消息接收。 gen_server 截获此消息,调用terminate/2 回调函数,然后调用terminates with shutdown,最终将信号传播到所有链接进程。

如果避免使用link to workers 不是一种选择,修复此错误的方法是取消terminate handler 中的所有工作人员的链接。

【讨论】:

    【解决方案2】:

    如何停止应用程序?也许主管应该有一个 stop/1 功能?例如,参见

    http://www.erlang.org/doc/apps/kernel/application.html#stop-1

    【讨论】:

    • 我只是做 application:stop(test_app).
    • 如果 test_app_sup.erl 和 test_app.erl 没有 stop/1 函数,那可能是问题所在。
    猜你喜欢
    • 2011-02-09
    • 1970-01-01
    • 2021-05-30
    • 1970-01-01
    • 2011-06-15
    • 2017-03-04
    • 1970-01-01
    • 1970-01-01
    • 2023-03-27
    相关资源
    最近更新 更多