【发布时间】:2016-06-22 17:43:00
【问题描述】:
目前我正在使用lists:foreach 和spawn_link 为项目启动可变数量的“工人”,即在启动时确定的工人数量。我希望每个工作人员都是一个 gen_server,这样我就可以在其中调用异步或同步消息(gen_server:cast 等)这可能吗?
【问题讨论】:
标签: erlang
目前我正在使用lists:foreach 和spawn_link 为项目启动可变数量的“工人”,即在启动时确定的工人数量。我希望每个工作人员都是一个 gen_server,这样我就可以在其中调用异步或同步消息(gen_server:cast 等)这可能吗?
【问题讨论】:
标签: erlang
是的,这是可能的。
您可以使用 simple_one_for_one: http://erlang.org/doc/man/supervisor.html#start_child-2
一个简化的 one_for_one 主管,其中所有子进程都是 动态添加相同进程类型的实例。
这是一个代码示例: master.erl 是主管:
-module(master).
-behaviour(supervisor).
%% API
-export([start_link/0]).
%% Supervisor callbacks
-export([init/1]).
-define(SERVER, ?MODULE).
start_link() ->
supervisor:start_link({local, ?SERVER}, ?MODULE, []).
init([]) ->
RestartStrategy = simple_one_for_one,
MaxRestarts = 1000,
MaxSecondsBetweenRestarts = 3600,
SupFlags = {RestartStrategy, MaxRestarts, MaxSecondsBetweenRestarts},
Restart = permanent,
Shutdown = 2000,
Type = worker,
AChild = {'worker', {'worker', start_link, []},
Restart, Shutdown, Type, ['worker']},
{ok, {SupFlags, [AChild]}}.
worker.erl 是童工:
-module(worker).
-behaviour(gen_server).
%% API
-export([start_link/0]).
-export([start_link/1]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-define(SERVER, ?MODULE).
-record(state, {}).
start_link() ->
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
start_link(I) ->
ServerName = lists:flatten(io_lib:format("~p~p", [?SERVER, I])),
io:format("I am ~p~n", [list_to_atom(ServerName)]),
gen_server:start_link({local, list_to_atom(ServerName)}, ?MODULE, [], []).
init([]) ->
{ok, #state{}}.
handle_call(_Request, _From, State) ->
Reply = ok,
{reply, Reply, State}.
handle_cast(calc, State) ->
io:format("result 2+2=4~n"),
{noreply, State};
handle_cast(calcbad, State) ->
io:format("result 1/0~n"),
1 / 0,
{noreply, State};
handle_cast(_Msg, State) ->
{noreply, State}.
handle_info(_Info, State) ->
{noreply, State}.
terminate(_Reason, _State) ->
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
在 erlang shell 中:
22> master:start_link().
{ok,<0.2475.0>}
23> lists:map(fun(X) -> supervisor:start_child(master, [X]) end, lists:seq(1, 10)).
【讨论】:
没有什么可以阻止您调用my_worker:start_link 而不是spawn_link。
在工人中
-module(my_worker).
-behaviour(gen_server).
%% API
-export([start_link/1]).
%% gen_server callbacks
-export([init/1, ...]).
%% API
start_link(Arg) ->
gen_server:start_link(?MODULE, Arg, []).
%% gen_server callbacks
init(Arg) ->
...
然后你就可以启动它了
[ {ok, _Pid} = my_worker:start_link(Arg) || Arg <- Args ].
如果您想将他们置于主管之下:
-module(my_sup).
-behaviour(supervisor).
%% API
-export([start_link/1]).
%% supervisor callbacks
-export([init/1]).
%% API
start_link(Argg) ->
gen_server:start_link(?MODULE, Args).
%% supervisor callbacks
init(Args) ->
Sup_flags = #{strategy => one_for_one, intensity => 1, period => 5},
Child_specs = [ #{id => Id, start => MFA}
|| {Id, {_M, _F, _A} = MFA} <- Args ],
{ok, {Sup_flags, Child_specs}}.
您可以从application:get_env/1,2,3 或数据库或其他任何地方读取他们的配置。之后您可以使用supervisor:start_child/2 启动它们。您可以使用simple_one_for_one 等。这只是一个过程。
【讨论】: