Erlang 在不事先知道N的情况下,在主管中声明N个工人的最佳方法

Erlang 在不事先知道N的情况下,在主管中声明N个工人的最佳方法,erlang,otp,Erlang,Otp,我正在开发一个有1个主管和几个工人的应用程序。这些工作人员中的每一个人只需打开一个tcp套接字,执行一个侦听,然后接受连接,在每个客户机到达时为其生成一个进程(我不介意监督这些进程) 我想在应用程序配置中配置监听地址,这样我就可以根据需要监听任意多个地址(至少需要一个地址,但可以指定任意数量)。每个地址都是一个ip(或主机名)和一个端口地址。到目前为止,没有什么新鲜事 我的问题是关于如何申报、启动和监督数量不详的工人。我的解决方案是(在运行时)在supervisor代码中动态生成init/1函数

我正在开发一个有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 }}.