你如何与simple_one_for_one
主管一起生孩子?
我正在阅读LYSE书籍,目前正在动态监督部分:http://learnyousomeerlang.com/supervisors#dynamic-supervision
作者按如下方式启动孩子,但我不知道djembe
在哪里定义:
1> supervisor:start_child(band_supervisor, [djembe, good]).
Musician Janet Tennelli, playing the djembe entered the room
{ok,<0.690.0>}
2> supervisor:start_child(band_supervisor, [djembe, good]).
{error,{already_started,<0.690.0>}}
这是我的尝试:
2> supervisor:start_link(band_supervisor, jamband).
{ok,<0.40.0>}
3> supervisor:start_child(band_supervisor, [djembe, good]).
=ERROR REPORT==== 28-Feb-2016::03:52:56 ===
** Generic server <0.40.0> terminating
** Last message in was {'EXIT',<0.33.0>,
{{noproc,
{gen_server,call,
[band_supervisor,
{start_child,[djembe,good]},
infinity]}},
[{gen_server,call,3,
[{file,"gen_server.erl"},{line,212}]},
{erl_eval,do_apply,6,
[{file,"erl_eval.erl"},{line,673}]},
{shell,exprs,7,[{file,"shell.erl"},{line,686}]},
{shell,eval_exprs,7,
[{file,"shell.erl"},{line,641}]},
{shell,eval_loop,3,
[{file,"shell.erl"},{line,626}]}]}}
** When Server state == {state,
{<0.40.0>,band_supervisor},
simple_one_for_one,
[{child,undefined,jam_musician,
{musicians,start_link,[]},
temporary,1000,worker,
[musicians]}],
undefined,3,60,[],band_supervisor,jamband}
** Reason for termination ==
** {{noproc,{gen_server,call,
[band_supervisor,
{start_child,[djembe,good]},
infinity]}},
[{gen_server,call,3,[{file,"gen_server.erl"},{line,212}]},
{erl_eval,do_apply,6,[{file,"erl_eval.erl"},{line,673}]},
{shell,exprs,7,[{file,"shell.erl"},{line,686}]},
{shell,eval_exprs,7,[{file,"shell.erl"},{line,641}]},
{shell,eval_loop,3,[{file,"shell.erl"},{line,626}]}]}
** exception exit: {noproc,{gen_server,call,
[band_supervisor,
{start_child,[djembe,good]},
infinity]}}
in function gen_server:call/3 (gen_server.erl, line 212)
以下是主管的看法:
-module(band_supervisor).
-behaviour(supervisor).
-export([start_link/1]).
-export([init/1]).
start_link(Type) ->
supervisor:start_link({local,?MODULE}, ?MODULE, Type).
%% The band supervisor will allow its band members to make a few
%% mistakes before shutting down all operations, based on what
%% mood he's in. A lenient supervisor will tolerate more mistakes
%% than an angry supervisor, who'll tolerate more than a
%% complete jerk supervisor
init(lenient) ->
init({one_for_one, 3, 60});
init(angry) ->
init({rest_for_one, 2, 60});
init(jerk) ->
init({one_for_all, 1, 60});
init(jamband) ->
{ok, {{simple_one_for_one, 3, 60},
[{jam_musician,
{musicians, start_link, []},
temporary, 1000, worker, [musicians]}
]}};
init({RestartStrategy, MaxRestart, MaxTime}) ->
{ok, {{RestartStrategy, MaxRestart, MaxTime},
[{singer,
{musicians, start_link, [singer, good]},
permanent, 1000, worker, [musicians]},
{bass,
{musicians, start_link, [bass, good]},
temporary, 1000, worker, [musicians]},
{drum,
{musicians, start_link, [drum, bad]},
transient, 1000, worker, [musicians]},
{keytar,
{musicians, start_link, [keytar, good]},
transient, 1000, worker, [musicians]}
]}}.
这里是音乐家:
-module(musicians).
-behaviour(gen_server).
-export([start_link/2, stop/1]).
-export([init/1, handle_call/3, handle_cast/2,
handle_info/2, code_change/3, terminate/2]).
-record(state, {name="", role, skill=good}).
-define(DELAY, 750).
start_link(Role, Skill) ->
gen_server:start_link({local, Role}, ?MODULE, [Role, Skill], []).
stop(Role) -> gen_server:call(Role, stop).
init([Role, Skill]) ->
%% To know when the parent shuts down
process_flag(trap_exit, true),
%% sets a seed for random number generation for the life of the process
%% uses the current time to do it. Unique value guaranteed by now()
random:seed(now()),
TimeToPlay = random:uniform(3000),
Name = pick_name(),
StrRole = atom_to_list(Role),
io:format("Musician ~s, playing the ~s entered the room~n",
[Name, StrRole]),
{ok, #state{name=Name, role=StrRole, skill=Skill}, TimeToPlay}.
handle_call(stop, _From, S=#state{}) ->
{stop, normal, ok, S};
handle_call(_Message, _From, S) ->
{noreply, S, ?DELAY}.
handle_cast(_Message, S) ->
{noreply, S, ?DELAY}.
handle_info(timeout, S = #state{name=N, skill=good}) ->
io:format("~s produced sound!~n",[N]),
{noreply, S, ?DELAY};
handle_info(timeout, S = #state{name=N, skill=bad}) ->
case random:uniform(5) of
1 ->
io:format("~s played a false note. Uh oh~n",[N]),
{stop, bad_note, S};
_ ->
io:format("~s produced sound!~n",[N]),
{noreply, S, ?DELAY}
end;
handle_info(_Message, S) ->
{noreply, S, ?DELAY}.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
terminate(normal, S) ->
io:format("~s left the room (~s)~n",[S#state.name, S#state.role]);
terminate(bad_note, S) ->
io:format("~s sucks! kicked that member out of the band! (~s)~n",
[S#state.name, S#state.role]);
terminate(shutdown, S) ->
io:format("The manager is mad and fired the whole band! "
"~s just got back to playing in the subway~n",
[S#state.name]);
terminate(_Reason, S) ->
io:format("~s has been kicked out (~s)~n", [S#state.name, S#state.role]).
%% Yes, the names are based off the magic school bus characters
%% 10 names!
pick_name() ->
%% the seed must be set for the random functions. Use within the
%% process that started with init/1
lists:nth(random:uniform(10), firstnames())
++ " " ++
lists:nth(random:uniform(10), lastnames()).
firstnames() ->
["Valerie", "Arnold", "Carlos", "Dorothy", "Keesha",
"Phoebe", "Ralphie", "Tim", "Wanda", "Janet"].
lastnames() ->
["Frizzle", "Perlstein", "Ramon", "Ann", "Franklin",
"Terese", "Tennelli", "Jamal", "Li", "Perlstein"].
答案 0 :(得分:1)
learnyousomeerlang.com
是一个很好的信息来源,但有时我觉得它太精细了。我通过阅读supervisor OTP文档直接了解了OTP的这一部分。如果你只想了解动态孩子,你可能不需要gen_server
。见my simple implementation:
-module(estp_proj_sup).
-behaviour(supervisor).
%% API
-export([start_link/0]).
%% Supervisor callbacks
-export([init/1, add/2]).
-define(SERVER, ?MODULE).
start_link() ->
supervisor:start_link({local, ?SERVER}, ?MODULE, []).
init([]) ->
Child = ?WORKER(estp_project),
{ok, {{simple_one_for_one, 3, 30}, [Child]}}.
add(Name, Cfg) ->
Server = estp_project:server_name(Name),
State = [{name, Name}|Cfg],
case whereis(Server) of
undefined ->
add_child(Server, State);
Pid ->
delete_child(Server, Pid),
add_child(Server, State)
end.
add_child(Server, State) ->
supervisor:start_child(?SERVER, [Server, State]).
delete_child(Server, Pid) ->
ok = supervisor:terminate_child(?SERVER, Pid).
其中动态子名estp_project:server_name/1
的创建方式如下:
server_name(Name) ->
Module = atom_to_binary(?MODULE, utf8),
Binary = atom_to_binary(Name, utf8),
binary_to_atom(<<Module/binary, <<"$">>/binary, Binary/binary>>, utf8).
和worker
定义为:
-define(SHUTDOWN_TIMEOUT, 5000).
-define(WORKER(I), {I, {I, start_link, []}, permanent, ?SHUTDOWN_TIMEOUT, worker, [I]}).
然后,您只需拨打this code中的estp_proj_sup:add(Name, Cfg)
来添加儿童:
process(Res) ->
%% Res contains parsed list of project names and their configurations
[set_project(Name, Cfg) || {Name, {ok, Cfg}} <- Res].
set_project(Name, Cfg) ->
case estp_proj_sup:add(Name, Cfg) of
{ok, _Pid} -> ok;
{error, _} = Err -> Err
end.
无论如何,我尝试了你的例子,它似乎正在起作用:
4> {ok, S} = band_supervisor:start_link(jamband).
{ok,<0.47.0>}
5> supervisor:start_child(band_supervisor, [djembe, good]).
Musician Wanda Terese, playing the djembe entered the room
{ok,<0.49.0>}
Wanda Terese produced sound!
Wanda Terese produced sound!
Wanda Terese produced sound!
Wanda Terese produced sound!
Wanda Terese produced sound!
6> supervisor:start_child(band_supervisor, [djembe, good]).
{error,{already_started,<0.49.0>}}
Wanda Terese produced sound!
Wanda Terese produced sound!