4

现在,假设我们正在设计一个应用程序,由 2 个 Erlang 节点组成。在节点 A 上,会有非常多的进程,数量级为数千。这些进程通过向节点 B 上的已注册进程发送消息来访问节点 B 上的资源。

在节点 B,假设您通过执行以下函数启动了一个进程:

start_server()->
    register(zeemq_server,spawn(?MODULE,server,[])),ok.<br>
server()-&gt;
    receive
            {{CallerPid, Ref}, {Module, Func, Args}} -&gt;
                    Result = (catch erlang:apply(Module, Func, Args)),
                    CallerPid ! {Ref, Result},
                    server();
            _ -&gt; server()
    end.

在节点 A 上,任何想要在节点 B 上执行给定模块中的任何功能的进程都使用以下代码:

call(Node, Module, Func, Args)-&gt;
        Ref = make_ref(),
        Me = self(),
        {zeemq_server,Node} ! {{Me, Ref}, {Module, Func, Args}},
        receive
                {Ref, Result} -&gt; Result
        after timer:minutes(3) -&gt; 
            error_logger:error_report(["Call to server took so long"]),
            {error,remote_call_failed}
        end.

所以假设zeemq_serverNode B上的进程永远不会down,并且Node A和B之间的网络连接一直是up的,请回答以下问题:

Qn 1:由于Node B上只有一个接收进程,它的邮箱最有可能是满的,一直。这是因为,节点 A 上的进程很多,并且在给定的时间间隔内,例如 2 秒,每个进程至少对节点 B 服务器进行一次调用。可以通过哪些方式使节点 B 上的接收变得冗余?,例如进程组等并解释(概念)这将如何替换上面的服务器端代码。显示客户端会发生什么变化。

Qn 2: 在节点 B 上只有一个接收者的情况下,进程邮箱中允许的最大消息数是多少?如果单个进程邮件牛被太多消息淹没,erlang 将如何响应?

Qn 3:使用上面显示的概念,我可以通过什么方式保证每个发送请求的进程在超时发生之前尽快得到答案?将节点 B 上的接收部分转换为并行操作有帮助吗?像这样:

start_server()-&gt;
    register(zeemq_server,spawn(?MODULE,server,[])),ok.<br>
server()-&gt;
    receive
            {{CallerPid, Ref}, {Module, Func, Args}} -&gt;
                    <b>spawn(?MODULE,child,[Ref,CallerPid,{Module, Func, Args}]),</b>
                    server();
            _ -&gt; server()
    end.    
child(Ref,CallerPid,{Module, Func, Args})-&gt;
    Result = (catch erlang:apply(Module, Func, Args)),
    CallerPid ! {Ref, Result},
    ok.

如上所示的方法,可能会增加Node B上运行的瞬时进程数,这可能会因内存而对服务造成很大影响。但是,它看起来不错,并使server()循环立即返回以处理下一个请求。您对此修改有何看法?

最后:说明您将如何Pool of receiver Threads在节点 B 上实现 a,但在节点 A 上似乎低于一个Name。这样,传入消息在接收线程之间多路复用,并且在这组进程中共享负载。保持问题的含义相同。

4

2 回答 2

2

进程邮箱中的最大消息数不受限制,内存量除外。

此外,如果您需要检查邮箱大小,请使用

erlang:process_info(self(),[message_queue_len,messages]).

这将返回如下内容:

[{message_queue_len,0},{messages,[]}]

我建议您首先将上面的服务器转换为 gen_server。这是你的工人。

接下来,我建议使用 poolboy ( https://github.com/devinus/poolboy ) 创建一个服务器实例池作为 poolboy 工作者(在他们的 github Readme.md 中有示例)。最后,我建议使用辅助方法为调用者创建一个模块,该方法创建一个 poolboy 事务并将池中的 Worker arg 应用于函数。以下示例来自他们的 github:

squery(PoolName, Sql) ->
    poolboy:transaction(PoolName, fun(Worker) ->
                                     gen_server:call(Worker, {squery, Sql})
                                  end).

也就是说,Erlang RPC 会更好地满足您的需求吗?有关 Erlang RPC 的详细信息,请访问 http://www.erlang.org/doc/man/rpc.html。在http://learnyousomeerlang.com/distribunomicon#rpc可以找到对 Erlang RPC 的良好处理。

于 2012-11-16T13:10:49.360 回答
0

IMO 产生一个新的进程来处理每个请求可能是矫枉过正,但如果不知道每个请求必须做什么就很难说。

您可以有一个进程池来处理每个 msg,使用循环方法分发请求或根据请求的类型来处理它,将其发送到子进程或产生一个进程。您还可以通过查看它们的 msg 队列并在它们过载时启动新的子进程来监视池进程的负载。使用主管.. 只需在 init 中使用 send_after 每隔几秒钟监控一次负载并采取相应措施。如果可以,请使用 OTP,虽然有开销,但值得。

我不会使用 http 进行专线通信,我相信它的开销太大。您可以使用进程池来控制负载来处理它。

于 2018-03-10T21:27:05.477 回答