【问题标题】:What process executes rpc:cast?什么进程执行 rpc:cast?
【发布时间】:2018-03-08 02:40:59
【问题描述】:

现在我正在构建一个分布式应用程序,并且正在研究使用 rpc:cast 与 rpc:call,因为我不希望调用进程等待返回值,甚至接收一个返回值。

Cast 完美地解决了这个问题,但我很好奇,什么进程执行 rpc:cast 传递的函数并调用?我应该担心并发过多/不足吗?

通常,当我想要另一个进程执行代码时,我会使用一组工作人员,并使用 gen_server:cast/2 向其中一个工作人员发送异步强制转换,然后让他们处理它。但是对于 RPC,它并没有定义什么进程进行计算。进程是否已预先分配并等待?还是每个进程都在 rpc 调用时生成然后终止?根据我的阅读,如果您要进行多次调用,这是非常低效的,因为进程只是被创建和销毁,这在计算上并不容易。

提前感谢您的帮助!

【问题讨论】:

    标签: concurrency erlang


    【解决方案1】:

    tl;博士:

    在目标节点上生成一个进程来处理 RPC。

    讨论

    如果我们看一下 rpc 模块的代码,我们会发现它在 disterl 中封装了底层正常的节点间消息传递功能:

    https://github.com/erlang/otp/blob/master/lib/kernel/src/rpc.erl#L398-L403

    -spec cast(Node, Module, Function, Args) -> true when
          Node :: node(),
          Module :: module(),
          Function :: atom(),
          Args :: [term()].
    
    cast(Node, Mod, Fun, Args) when Node =:= node() ->
        catch spawn(Mod, Fun, Args),
        true;
    cast(Node, Mod, Fun, Args) ->
        gen_server:cast({?NAME,Node}, {cast,Mod,Fun,Args,group_leader()}),
        true.
    

    发生的情况是,当在当前节点上调用 cast 时,会生成一个 新进程,并使用提供的 MFA 来执行。如果在不同的节点上调用案例,则通过gen_server:cast/2 向指定节点的组长发送一条消息,目标节点上的组长将生成一个处理 MFA 的进程。

    演员的处理代码在这里:

    https://github.com/erlang/otp/blob/master/lib/kernel/src/rpc.erl#L130-L139

    -spec handle_cast(term(), state()) -> {'noreply', state()}.
    
    handle_cast({cast, Mod, Fun, Args, Gleader}, S) ->
        spawn(fun() ->
              set_group_leader(Gleader),
              apply(Mod, Fun, Args)
          end),
        {noreply, S};
    handle_cast(_, S) ->
        {noreply, S}. % Ignore !
    

    如果您已经知道要将消息发送到另一个节点上的哪个进程,那么将节点的网格视为超级节点可能更有意义,绕过完全 rpc 模块,只需进行 gen_server 调用或直接对其进行转换。当然,使用哪种方法取决于品味和架构。

    【讨论】:

    • 因此发送大量的 rpc 通信可能不是一个好主意,因为每次 node1 从 node2 接收到一个 rpc 时,它都必须产生一个全新的进程,我已经读过这是密集的。
    • 您建议了一个替代方案,以获取有关节点的预定义信息并向其发送 gen_server:cast() - 理想情况下,尽管您希望使用池以便 1) 不会产生新进程(时间关注)2)如果消息涌入,那么并发性是有限的。那么你怎么知道进程id,如果要获得进程id,你必须首先从node2咨询node1上某种形式的grpoc_get_worker?是否有任何普遍接受的解决方法,或者它是一种“修复”,每个人都有不同的做法?
    • @Ryan 实际上,我有好消息要告诉你! Erlang 中的生成进程在时间、内存和 CPU 方面非常轻量。与另一个节点交谈所涉及的网络通信和延迟比rex 正在做的工作要昂贵得多。因此,即使在另一边只有一个rex,您也可以通过请求向它发送垃圾邮件而无需过多担心——rex 的工作开销可以忽略不计。如果您有非常复杂的计算,而 RPC 非常适合,那么转换成本是您最不关心的开销问题。
    • @Ryan 至于“替代方案”,您可以直接在另一个节点上生成进程,这就是为什么spawn/2spawn/4 接受Node 参数(erlang.org/doc/man/erlang.html#spawn-2)。如果不知道作业是什么,负载是什么样的等等,所有这些都无法做出很好的判断。操作的上下文就是一切。无论您在做什么,编写一些测试用例以查看它们如何对您的负载做出反应应该非常简单。您可以来Erlang/OTP chat与我们讨论。
    • 非常感谢。你可以看出我对所有这些过程都感到担忧,但这似乎没什么大不了的。我不记得我在哪里读到进程在 erlang 中有很高的开销,但我现在很高兴知道这不是一个大问题。感谢您的帮助!
    【解决方案2】:

    进程的名称是rex。它是一个注册进程,它产生一个执行apply(M, F, A) 的进程。

    https://github.com/erlang/otp/blob/master/lib/kernel/src/rpc.erl#L28

    https://github.com/erlang/otp/blob/master/lib/kernel/src/rpc.erl#L402

    顺便说一句,这意味着所有rpc:call/4rpc:cast/4 以及其他参数都经过一个限制可扩展性的过程。 rpc 模块不适用于高吞吐量目的。

    【讨论】:

    • 它们通过rex,但rex 产生一个单独的工作人员来实际执行每个工作,除非它被发送一个call_block(然后rex 自己执行)。跨度>
    • 是的,你是对的。 rex 产生一个真正起作用的进程。我编辑答案。
    • 因为 Erlang 的生成速度如此之快,我认为最大的性能瓶颈惊喜将是人们在发送大消息和小消息之间遇到的差异。如果 rpc 调用的参数在字节方面很大,您可能会开始出现奇怪的行为。或者,如果连接速度慢或延迟高。 RPC 变得非常复杂,但我认为生成部分是这里所有开销中最小的(大约 8 微秒左右,网络延迟通常会比这高)。
    猜你喜欢
    • 1970-01-01
    • 2016-05-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-12
    • 1970-01-01
    • 2015-05-29
    • 2021-09-12
    相关资源
    最近更新 更多