在不事先知道N的情况下,在主管中申报N个工人的最佳方式

mar*_*log 2 erlang erlang-otp

我正在开发一个包含1名主管和几名工人的应用程序.这些工作人员中的每一个都只是打开一个tcp套接字,执行监听,然后接受连接,为每个客户端到达时生成一个进程(我不介意监督这些).

我想在应用程序配置中配置侦听地址,因此我可以根据需要使用尽可能多的地址进行侦听(至少需要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 }}.
Run Code Online (Sandbox Code Playgroud)

这允许我为每个worker动态生成一个名称,并生成worker定义.所以:

  1. 这个解决方案可以接受 在我看来,它并不那么优雅.对于这种用例,是否有任何标准解决方案(或最佳实践等)?

  2. 我知道"simple_one_for_one"策略,它允许动态地将工作人员添加到主管.但它是否也可以用来动态生成工人的名字?是否更好(以任何方式)使用"simple_one_for_one"而不是我自己使用"one_for_one"的解决方案?(再次,对于这种特殊情况).

在此先感谢,并为长篇帖子感到抱歉!:)

nia*_*hoo 5

simple_one_for_one:

在worker的init函数中,您可以在表中注册它们以将名称与其PID相关联,这样您就可以从名称中获取pid.

您可以使用global模块(或gproc!)将名称与pid相关联.当进程终止时,名称会自动删除globalgproc,因此当主管重新启动子进程时,名称可用.

您可以在主管的参数列表(第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 }}.
Run Code Online (Sandbox Code Playgroud)

或者使用一个lists:foldl代替所有的小函数来保持代码简短.

但无论如何,我会在init的Args列表中传递地址,而不是get_env在init内调用以保持其纯净.