我正在尝试让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").
答案 0 :(得分:1)
当您尝试使用宏调用?CHILD(foo, worker)
启动具有宏的子项时,使用宏调用?CHILD(foo, permanent)
检查childspec。 CHILD
宏的第二个参数是流程类型,它应该是worker
或supervisor
。所以第一次宏调用是正确的。值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
,但我读到它应该用于必须在死亡前完成任务的过程。