erlang 原生支持rpc

erlang 原生支持rpc(rex)

1.rex 是什么?

rexkernrl进程树第2个启动的进程
主要提供rpc服务,系统的所有rpc远程调用,都会经过rex

2.rex 创建

% kernel.erl
 Rpc = #{id => rex,
                    start => {rpc, start_link, []},
                    restart => permanent,
                    shutdown => 2000,
                    type => worker,
                    modules => [rpc]},
...

3.rex是否是热点?

所有的rpc请求都要经过rex进程,很容易形成热点,
而且是以节点(node)为单位的堵塞,换句话说,如果N个Node同时向同一个Node发起rpc请求,那么所有的消息都在同一个rex进程堆积。
是否有替代方案?有,请参照https://github.com/discordapp/gen_rpc.git

4.rpc:callrpc:block_call的区别

# rpc:call

do_call(Node, Request, infinity) ->
    rpc_check(catch gen_server:call({?NAME,Node}, Request, infinity));
do_call(Node, Request, Timeout) ->
    Tag = make_ref(),
    {Receiver,Mref} =
    erlang:spawn_monitor(
      fun() ->
          %% Middleman process. Should be unsensitive to regular
          %% exit signals.
          process_flag(trap_exit, true),
          Result = gen_server:call({?NAME,Node}, Request, Timeout),
          exit({self(),Tag,Result})
      end),
      % 这里不直接使用 gen_server:call 是因为gen_server:call可能让调用线程崩溃(exit)
      % 但是换句话来说,可以使用catch语句捕获异常,防止错误
      % 这样子还可以节约一个调度
      
    receive
    {'DOWN',Mref,_,_,{Receiver,Tag,Result}} ->
        rpc_check(Result);
    {'DOWN',Mref,_,_,Reason} ->
        %% The middleman code failed. Or someone did 
        %% exit(_, kill) on the middleman process => Reason==killed
        rpc_check_t({'EXIT',Reason})
    end.

handle_call({call, Mod, Fun, Args, Gleader}, To, S) ->
    handle_call_call(Mod, Fun, Args, Gleader, To, S);
...
handle_call_call(Mod, Fun, Args, Gleader, To, S) ->
    %% Spawn not to block the rpc server.
    {Caller,_} =
    erlang:spawn_monitor(
      fun () ->
          set_group_leader(Gleader),
          Reply = 
              %% in case some sucker rex'es 
              %% something that throws
              case catch apply(Mod, Fun, Args) of
              {'EXIT', _} = Exit ->
                  {badrpc, Exit};
              Result ->
                  Result
              end,
          gen_server:reply(To, Reply)
      end),
     % 在目标Node上新开一个proc来处理rpc请求
    {noreply, maps:put(Caller, To, S)}.

% rpc:block_call
handle_call({block_call, Mod, Fun, Args, Gleader}, _To, S) ->
    MyGL = group_leader(),
    set_group_leader(Gleader),
    Reply = 
    case catch apply(Mod,Fun,Args) of
        {'EXIT', _} = Exit ->
        {badrpc, Exit};
        Other ->
        Other
    end,
    group_leader(MyGL, self()), % restore
    % 这个会真正堵塞rex,所以尽量不要使用
    {reply, Reply, S};

所以rpc:call其实算是异步调用,切记不要使用rpc:block_call,除非遇到特殊情况

5.如何减少rpc的热点?

  • 尽量使用 gen_server:call({PID,Node}, Request, Timeout)来代替rpc:call(M,F,A)
  • 尽量自己建立类似于rex的进程,然后使用负载
  • 如果使用的是cast,请尽量合并请求,减少消息数量

6. 总结

虽然系统提供了rpc的原始支持,但是确带来了另外一个热点问题。

但是反观erlangActor思想,我们会发现,我们在日常的使用中,rpc是一种不符合Actor思想的做法。
个人认为真正纯粹的Actor,就是给对面Node上的proc发送一个消息,然后等到消息回执就可以了,直接使用gen_server:call就可以满足,而且不会有任何的热点问题。

7.参考

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • erlang常规面试题 基础 消息发送 基础相关 OTP相关 gen_server:cast和erlang:sen...
    randyjia阅读 4,275评论 1 3
  • 世界是并行的,Erlang程序反应了我们思考和交流的方式,人作为个体通过发送消息进行交流,如果有人死亡,其他人会注...
    abel_cao阅读 2,795评论 1 4
  • 分布式 Erlang Erlang 自带分布式功能,并且 Erlang 语言的消息发送也完全适应分布式环境。我们称...
    Shawn_xiaoyu阅读 8,528评论 6 12
  • erlang应用脚本stop分析 其实这篇文章的名字应该是如何安全关闭erlang应用更加科学。 erlang应用...
    randyjiawenjie阅读 1,430评论 0 1
  • 并发 创建进程 使用 erlang:spawn/1,2,3,4 用来创建一个 erlang 进程。Erlang 进...
    Shawn_xiaoyu阅读 13,959评论 9 22