Erlang 在不事先知道N的情况下,在主管中声明N个工人的最佳方法
我正在开发一个有1个主管和几个工人的应用程序。这些工作人员中的每一个人只需打开一个tcp套接字,执行一个侦听,然后接受连接,在每个客户机到达时为其生成一个进程(我不介意监督这些进程) 我想在应用程序配置中配置监听地址,这样我就可以根据需要监听任意多个地址(至少需要一个地址,但可以指定任意数量)。每个地址都是一个ip(或主机名)和一个端口地址。到目前为止,没有什么新鲜事 我的问题是关于如何申报、启动和监督数量不详的工人。我的解决方案是(在运行时)在supervisor代码中动态生成init/1函数的结果,如下所示:Erlang 在不事先知道N的情况下,在主管中声明N个工人的最佳方法,erlang,otp,Erlang,Otp,我正在开发一个有1个主管和几个工人的应用程序。这些工作人员中的每一个人只需打开一个tcp套接字,执行一个侦听,然后接受连接,在每个客户机到达时为其生成一个进程(我不介意监督这些进程) 我想在应用程序配置中配置监听地址,这样我就可以根据需要监听任意多个地址(至少需要一个地址,但可以指定任意数量)。每个地址都是一个ip(或主机名)和一个端口地址。到目前为止,没有什么新鲜事 我的问题是关于如何申报、启动和监督数量不详的工人。我的解决方案是(在运行时)在supervisor代码中动态生成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定义。因此:
提前谢谢,很抱歉发了这么长的帖子!:) 从您的代码中,我知道您希望从环境中获取子对象的数量。 在rabbitmq开源项目的worker_pool_sup.erl的源代码中,我读到了几乎相似的需求代码,代码非常优雅,我认为对您有帮助。 3个文件是相关的,worker\u pool\u sup.erl、worker\u pool\u worker.erl、work\u pool.erl 以下代码来自worker\u pool\u 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)]]}}.
用
simple\u one\u for\u one
:
在worker的init函数中,您可以将它们注册到一个表中,以将名称与它们的PID相关联,这样您就可以从名称中获取PID
您可以使用global
模块(或!)将名称与pid关联。当一个进程终止时,global
或gproc
会自动删除该名称,因此当主管重新启动子进程时,该名称可用
您将在supervisor:start\u child/2的参数列表(第二个参数)中传递名称
使用simple\u one\u for\u one
将允许您在主管初始化后动态添加更多侦听器
如果您需要此功能,simple\u one\u for\u one
是一个很好的解决方案
如果您坚持使用sup的init函数中的动态内容解决方案,您可以像这样清理代码,它可能看起来更优雅,也可能不优雅:
-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 }}.
-定义(
子项(名称,Args),
{姓名{
?模块,启动\监听,Args
},永久,5000,工人,[?模块]
}
).
生成_名称(地址)->生成_名称(地址,1,[])。
生成_名称([],_,Acc)->Acc;
生成_名称([Addr|address],ID,Acc)->生成_名称(地址,ID+1,[{ID|u name(ID),Addr}|Acc])。
id\u名称(id)->“侦听器-”++整数\u到\u列表(id)。
初始化([])->
{ok,Addresses}=application:get_env(listen),
Children=[?CHILD(Name,Address)|{Name,Address}谢谢!我倾向于认为我的原始代码可读性更强,更简单(即:你不必动太多眼睛就能看到发生了什么),但+1表示建议将地址作为inithmm的参数传递。我有很多tcp工作人员使用一个侦听器的经验,但我没有这样做。我的直觉是,这种方法设计得不好。我改为遵循了这一点。基本上,只要你监听端口,你就会启动一个主管来监视一个工作人员。还有很长的路要走,伙计,不要不要着急。设计是最重要的,你需要花时间阅读所有内容。@bighostkim谢谢你提供的资源,我来看看:)我想要的不仅仅是复制和粘贴,但谢谢你指出来源
-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 }}.