二郎otp童工

时间:2013-10-22 23:27:52

标签: erlang otp

我正在尝试让OTP主管启动子工作者,这些子工作者将(最终)连接到远程服务器。我使用Rebar创建了一个模板测试应用程序,我试图让主管在模块'foo'中启动函数'hi'。它编译好并运行:

Eshell V5.8.5  (abort with ^G)
1> test_app:start(1,1).
{ok,<0.34.0>}

但是当我尝试启动工作人员时,它会变成梨形并出现此错误:

2> test_sup:start_foo().
{error,{badarg,{foo,{foo,start_link,[]},
                    permanent,5000,worker,
                    [foo]}}}

问题似乎与此问题类似,但不一样:Erlang - Starting a child from the supervisor module

有什么想法吗?

test_app.erl

-module(test_app).
-behaviour(application).net 
-export([start/2, stop/1]).

start(_StartType, _StartArgs) ->
    test_sup:start_link().

stop(_State) ->
    ok.

Test_sup.erl:

-module(test_sup).
-behaviour(supervisor).
-export([start_link/0]).
-export([init/1, start_foo/0]).
-define(CHILD(I, Type), {I, {I, start_link, []}, permanent, 5000, Type, [I]}).

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

init([]) ->
    {ok, { {one_for_one, 5, 10}, []} }.
start_foo()->
    supervisor:check_childspecs(?CHILD(foo, worker)),
    supervisor:start_child(?MODULE, ?CHILD(foo, permanent)). 

foo.erl:

-module(foo).
-export([hi/0]).
hi()->
io:format("worker ~n").

2 个答案:

答案 0 :(得分:1)

当您尝试使用宏调用?CHILD(foo, worker)启动具有宏的子项时,使用宏调用?CHILD(foo, permanent)检查childspec。 CHILD宏的第二个参数是流程类型,它应该是workersupervisor。所以第一次宏调用是正确的。值permanent重新启动类型的值,您已将其设置为permanent,因此第二次调用错误,您收到badarg错误

注意:库函数通常也会产生badarg错误,而不仅仅是内置函数。 为什么它并不总是显而易见的。

答案 1 :(得分:0)

我认为罗伯特的回答是不完整的,在用工作人员替换永久工作后,你仍然有supervisor:check_childspecs(?CHILD(foo, worker)),返回的错误,我不知道为什么。

<强> [编辑]

bard arg的问题来自...... badarg:o)

check_childspecs扩展了child_specs的列表,正确的语法是supervisor:check_childspecs([?CHILD(foo, worker)]),然后它工作正常。以下代码已更新。

[编辑结束]

但是你也会得到一个错误,因为主管会尝试启动foo模块中不存在的函数foo:start_link。  以下代码打印错误,但似乎工作正常。

-module(foo).
-export([hi/0,start_link/0,loop/0]).

start_link() ->
    {ok,spawn_link(?MODULE,loop,[])}.

hi()->
io:format("worker ~n").

loop() ->
    receive
        _ -> ok
    end.


-module(test_sup).
-behaviour(supervisor).
-export([start_link/0]).
-export([init/1, start_foo/0]).
-define(CHILD(I, Type), {I, {I, start_link, []}, permanent, 5000, Type, [I]}).

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

init([]) ->
    {ok, { {one_for_one, 5, 10}, []} }.
start_foo()->
    io:format("~p~n",[supervisor:check_childspecs([?CHILD(foo, worker)])]),
    supervisor:start_child(?MODULE, ?CHILD(foo, worker)). 

<强> [编辑]

回答大卫评论

在我的代码中,loop/0根本没有循环,在接收块上,进程等待任何消息,一旦收到消息,进程就会返回值ok。因此,只要工作进程没有收到任何消息,它就会继续生存,这对于与主管进行一些测试很好:o)。

相反,hi / 0功能只是在控制台上打印'worker'并完成。由于主管的重启策略为one_for_one,最大重启为5且子进程为永久性,主管将尝试启动hi进程5次,在控制台上打印五次'worker',然后它将放弃并使用错误消息** exception error: shutdown

终止自身

通常,您应该为永不结束的进程(例如应用程序的主服务器)选择permanent。对于通常在完成工作后立即死亡的流程,您应该使用temporary。我从未使用transient,但我读到它应该用于必须在死亡前完成任务的过程。