Erlang,尝试使用多个响应进行gen_server:调用

Erlang,尝试使用多个响应进行gen_server:调用,erlang,otp,gen-server,Erlang,Otp,Gen Server,尝试在项目中使用OTP样式,得到一个OTP接口问题。什么解决方案更受欢迎/美观 我所拥有的: 带有mochiweb的web服务器 一个过程产生了许多(1000-2000)孩子。 子级包含状态(网络流速度)。如果需要,处理将消息代理给子级并创建新的子级 在mochiweb中,我有一个页面,显示了所有演员的速度,乳清是如何制作的: nf_collector ! {get_abonents_speed, self()}, receive {abonents_speed_

尝试在项目中使用OTP样式,得到一个OTP接口问题。什么解决方案更受欢迎/美观

我所拥有的:

  • 带有
    mochiweb的web服务器
  • 一个过程产生了许多(1000-2000)孩子。 子级包含状态(网络流速度)。如果需要,处理将消息代理给子级并创建新的子级
  • 在mochiweb中,我有一个页面,显示了所有演员的速度,乳清是如何制作的:

        nf_collector ! {get_abonents_speed, self()},
        receive
            {abonents_speed_count, AbonentsCount} ->
                ok
        end,
    %% write http header, chunked
    %% and while AbonentsCount != 0,  receive speed and write http
    
    这不是opt风格,我怎么能理解。解决方案:

  • 在API同步函数中,以所有速度获取所有请求,并以所有速度返回列表。但我想马上给客户写
  • API函数的一个参数是回调:

    nf_collector:get_all_speeds(fun (Speed) -> Resp:write_chunk(templater(Speed)) end)
    
  • 返回迭代器: get_all_Speed(获取所有速度)的结果之一将与接收块一起运行。它的每次调用都将返回
    {ok,Speed}
    ,最后返回
    {end}

  • 从主管那里培养您的“孩子”:

    -module(ch_sup).
    -behaviour(supervisor).
    -export([start_link/0, init/1, start_child/1]).
    start_link() -> supervisor:start_link({local, ?MODULE}, ?MODULE, []).
    init([]) -> {ok, {{simple_one_for_one}, [{ch, {ch, start_link, []}, transient, 1000, worker, [ch]}]}}.
    start_child(Data) -> supervisor:start_child(?MODULE, [Data]).
    
    用ch_sup:Start_child/1启动它们(数据就是一切)

    将您的孩子实现为gen_服务器:

    -module(ch).
    -behaviour(gen_server).
    -record(?MODULE, {speed}).
    

    现在,您可以使用主管获取正在运行的孩子的列表并查询他们,尽管您必须接受孩子在获取孩子列表和给他们打电话之间死亡的可能性,显然,孩子可能出于某种原因还活着,但没有响应,或者响应时出错,等等

    上面的get_speed/2函数返回{ok,speed}或died或timeout。根据您的应用程序需要进行适当的筛选;简单的列表理解,这里有一些

    只是速度:

    [Speed || {ok, Speed} <- [ch:get_speed(Pid, 1000) || Pid <-
        [Pid || {undefined, Pid, worker, [ch]} <-
            supervisor:which_children(ch_sup)
            ]
        ]].
    

    [Speed | |{好的,Speed}实际的问题是什么?你在实现任何选项时有困难吗?如果有,你应该问一些与之相关的问题。否则,这将主要是基于观点的,完全取决于你的用例。我同意Adam的观点,认为这更像是一个设计问题,但没有足够的信息提供任何建议。为什么有1000-20个00个包含状态的子进程。为什么会有进程返回计数并发出调用,而不是返回这些子进程并让调用方决定要做什么?读/写比率是多少?非功能性需求是什么,比如低延迟或吞吐量更重要吗?是主要功能还是在re中占多大比例系统的st?等等。没有额外的信息对我来说没有多大意义。写这个问题的原因很简单:erlang提供了编写基于角色的程序的简单方法,OTP提供了标准化。首先,我编写了没有OTP的程序,理解程序逻辑很复杂。添加OTP后,它变得扁平和简单。她说我还有一个更复杂的行为,whan sync/async调用。我想问一下,如果遇到类似的问题,其他erlang开发人员会选择什么。或者这个问题是由糟糕的设计造成的,答案是-简单一点,没有1000个(或更多)Actor尝试重新表述:如果我的呼叫与多个参与者绑定,那么我必须处理许多情况:1.当我的呼叫接收消息时,一个或多个参与者死亡2.呼叫发起人死亡3.一次多打2-3个电话4.其他…OTP行为、良好的编码标准或我如何做到的建议在哪里?这是其他erlang编码人员关注的问题对我的代码来说,不要开始闪烁:)如果不是,是的,我可以通过多种方式来完成,所以…我可以结束这个问题感谢你的回答!你对每个孩子进行synchronius调用。我想对每个孩子进行异步调用,并得到结果。(或者,如果使用OTP规范,可以使用多个并行同步调用,就像你通过生成新演员所做的那样。)(我害怕生1000个孩子,你告诉我生1000个*:))为什么我认为standart调用是个坏主意:1.迭代所有孩子并按顺序调用-很慢2.如果每次同步调用都生演员-可能演员太多,进行同步调用,调用结果{ok,pid}在什么-发送速度之后,呼叫方获取所有pid,添加手表以监控死亡。我的解决方案可能是:我向主管打同步电话,他保存孩子列表以获取pid,发送给所有孩子获取速度(MyPid)并回答{ok,ChildsCount}。孩子向我发送他的速度。如果孩子死亡并且他的pid在保存的列表中,主管将发送给我{err,child_die}或其他什么,因为我可以计算答案的数量,他不知道什么。但是错误:1.主管何时可以删除保存的列表?2.如果我死了,孩子们如何理解什么需要停止发送速度?我重新阅读了OTP文档,并认为我的问题“奇怪”,需要更同步地执行此操作(并且更慢:)),但根据OPT原则。或更改我的应用程序的体系结构。我这里有一些代码(所有代码都是脏的,现在没有OTP,我尝试学习并重写它)如果有人对我的问题感兴趣,欢迎:我将您的答案标记为正确,因为它(并重新阅读OPT文档)请帮助我了解我的问题的奇怪设计。谢谢大家!我很高兴它帮助了你。也许为了完整性,我应该包括最后一个案例,但因为你的问题是关于移动到OTP,这意味着你的永久和瞬态有状态进程将是gen_服务器/gen_fsm。异步消息的真正威力是在你不知道的情况下实现的不需要响应,这样您就可以触发并忘记;没有任何东西会阻塞,因此它非常高效。一旦需要响应,您可以发送消息,并等待接收响应,然后再继续(这是gen_server call为您所做的),或者您也可以异步发送和接收消息。如果您希望OTP“合规性”,尽管此处的服务器将是gen_server或gen_fsm,并且您可以调用,因此无需重新设计。对于许多gen_server,您需要所有服务器的响应,在使用这些响应之前,逻辑答案类似于我后面几段中的代码。
    
    get_speed(Pid, Timeout) ->
        try
            gen_server:call(Pid, get, Timeout)
        catch
            exit:{timeout, _} -> timeout;
            exit:{noproc, _} -> died
        end
    .
    
    handle_call(get, _From, St) -> {reply, {ok, St#?MODULE.speed}, St} end.
    
    [Speed || {ok, Speed} <- [ch:get_speed(Pid, 1000) || Pid <-
        [Pid || {undefined, Pid, worker, [ch]} <-
            supervisor:which_children(ch_sup)
            ]
        ]].
    
    [{Pid, Speed} || {Pid, {ok, Speed}} <-
        [{Pid, ch:get_speed(Pid, 1000)} || Pid <-
            [Pid || {undefined, Pid, worker, [ch]} <-
                    supervisor:which_children(ch_sup)]
            ]
        ].
    
    [{Pid, Any} || {Pid, Any} <-
        [{Pid, ch:get_speed(Pid, 1000)} || Pid <-
            [Pid || {undefined, Pid, worker, [ch]} <-
                    supervisor:which_children(ch_sup)]
            ]
        ].
    
    get_speeds() ->
        ReceiverPid = self(),
        Ref = make_ref(),
        Pids = [Pid || {undefined, Pid, worker, [ch]} <-
                supervisor:which_children(ch_sup)],
        lists:foreach(
            fun(Pid) -> spawn(
                fun() -> ReceiverPid ! {Ref, ch:get_speed(Pid, 1000)} end
                ) end,
            Pids),
        receive_speeds(Ref, length(Pids), os_milliseconds(), 1000)
    .
    
    receive_speeds(_Ref, 0, _StartTime, _Timeout) ->
        [];
    receive_speeds(Ref, Remaining, StartTime, Timeout) ->
        Time = os_milliseconds(),
        TimeLeft = Timeout - Time + StartTime,
        receive
            {Ref, acc_timeout} ->
                [];
            {Ref, {ok, Speed}} ->
                [Speed | receive_speeds(Ref, Remaining-1, StartTime, Timeout)];
            {Ref, _} ->
                receive_speeds(Ref, Remaining-1, StartTime, Timeout)
        after TimeLeft ->
            []
        end
    .
    
    os_milliseconds() ->
        {OsMegSecs, OsSecs, OsMilSecs} = os:timestamp(),
        round(OsMegSecs*1000000 + OsSecs + OsMilSecs/1000)
    .