我正在开发一个带有1个主管和几个工人的应用程序。这些工作中的每一个都只是打开一个tcp套接字,执行一个listen,然后接受连接,为每个客户端到达时生成一个进程(我不介意监督这些)。
我想在应用程序配置中配置侦听地址,因此我可以根据需要使用尽可能多的地址进行侦听(至少需要1个地址,但可以指定任何数字)。每个地址都是ip(或主机名)和端口地址。到目前为止,没有什么新的。
我的问题是如何申报,启动和监督未知数量的工人。我的解决方案是在管理程序代码中动态生成(在运行时)init / 1函数的结果,如下所示:
-define(
CHILD(Name, Args),
{Name, {
?MODULE, start_listen, Args
}, permanent, 5000, worker, [?MODULE]
}
).
init([]) ->
{ok, Addresses} = application:get_env(listen),
Children = lists:map(
fun(Address) ->
{X1,X2,X3} = os:timestamp(),
ChildName = string:join([
"Listener-",
integer_to_list(X1),
integer_to_list(X2),
integer_to_list(X3)
], ""),
?CHILD(ChildName, [Address])
end,
Addresses
),
{ok, { {one_for_one, 5, 10}, Children }}.
这允许我为每个worker动态生成一个名称,并生成worker定义。所以:
这个解决方案可以接受吗?在我看来,它并不那么优雅。对于这种用例,是否有任何标准解决方案(或最佳实践等)?
我知道“simple_one_for_one”策略,它允许动态地将工作人员添加到主管。但它是否也可以用来动态生成工人的名字?是否更好(以任何方式)使用“simple_one_for_one”而不是我自己使用“one_for_one”的解决方案? (再次,此特殊情况)。
提前致谢,对不起这篇长篇文章感到抱歉! :)
答案 0 :(得分:5)
使用simple_one_for_one
:
在您的worker的init函数中,您可以在表中注册它们以将名称与其PID相关联,这样您就可以从名称中获取pid。
您可以使用global
模块(或gproc!)将名称与pid相关联。当进程终止时,global
或gproc
会自动删除该名称,因此当主管重新启动子进程时,名称可用。
您可以在主管的参数列表(第2个参数)中传递名称:start_child / 2
使用simple_one_for_one
将允许您在管理员初始化后动态添加更多侦听器。
如果您需要此功能,simple_one_for_one
是一个很好的解决方案。
如果你坚持使用你的sup函数内部的动态内容解决方案,你可以像这样清理代码,它可能,或者可能不会,看起来更优雅:
-define(
CHILD(Name, Args),
{Name, {
?MODULE, start_listen, Args
}, permanent, 5000, worker, [?MODULE]
}
).
generate_names(Adresses) -> generate_names(Adresses, 1, []).
generate_names([], _, Acc) -> Acc;
generate_names([Addr|Addresses], ID, Acc) -> generate_names(Addresses, ID+1, [{id_name(ID), Addr}|Acc]).
id_name(ID) -> "listener-" ++ integer_to_list(ID).
init([]]) ->
{ok, Addresses} = application:get_env(listen),
Children = [?CHILD(Name, Address) || {Name, Address} <- generate_names(Addresses)],
{ok, { {one_for_one, 5, 10}, Children }}.
或者使用lists:foldl
而不是所有的小函数来保持代码简短。
但无论如何我会在init的Args列表中传递地址,而不是在init中调用get_env
以保持其纯净。
答案 1 :(得分:0)
从您的代码中,我知道您希望从环境中获取子项的数量。 在rabbitmq开源项目的worker_pool_sup.erl的源代码中,我已经阅读了几乎相似的需求代码,并且代码非常优雅,我认为它对您有所帮助。 3个文件是相关的,worker_pool_sup.erl,worker_pool_worker.erl,work_pool.erl。
以下代码来自worker_pool_sup.erl。
init([WCount]) ->
{ok, {{one_for_one, 10, 10},
[{worker_pool, {worker_pool, start_link, []}, transient,
16#ffffffff, worker, [worker_pool]} |
[{N, {worker_pool_worker, start_link, [N]}, transient, 16#ffffffff,
worker, [worker_pool_worker]} || N <- lists:seq(1, WCount)]]}}.
关于如何使用它,以下代码来自worker_pool.erl
get_worker_pid(WId) ->
[{WId, Pid, _Type, _Modules} | _] =
lists:dropwhile(fun ({Id, _Pid, _Type, _Modules})
when Id =:= WId -> false;
(_) -> true
end,
supervisor:which_children(worker_pool_sup)),
Pid.