谈谈erlang的timeout.

enjolras1205

gen_server call timeout

erlang的gen_server call默认有超时, 若在指定的超时内没有收到返回. 则会exit(timeout).

gen_server call

gen.erl:160

do_call(Process, Label, Request, Timeout) when is_atom(Process) =:= false ->
    Mref = erlang:monitor(process, Process),

    %% OTP-21:
    %% Auto-connect is asynchronous. But we still use 'noconnect' to make sure
    %% we send on the monitored connection, and not trigger a new auto-connect.
    %%
    erlang:send(Process, {Label, {self(), Mref}, Request}, [noconnect]),

    receive
        {Mref, Reply} ->
            erlang:demonitor(Mref, [flush]),
            {ok, Reply};
        {'DOWN', Mref, _, _, noconnection} ->
            Node = get_node(Process),
            exit({nodedown, Node});
        {'DOWN', Mref, _, _, Reason} ->
            exit(Reason)
    after Timeout ->
            erlang:demonitor(Mref, [flush]),
            exit(timeout)
    end.

什么时候应该捕获timeout?

显然, 如果等待一段时间后, 没有收到消息, 有两种可能.

  1. 对端未能返回消息.
  2. 对端返回了消息. 但调用端尚未收到.

catch timeout异常和处理过期的{Mref, Reply}返回

如果捕获了timeout异常, 对方又返回了消息, 该消息还是会发送到调用者的pid信箱中.
若是一个gen_server, 需要在handle_info中处理(建议忽略)该消息. 避免因为没有在handle_info 中处理{Mref, Reply}消息而崩溃.

幂等

超时不意味着调用失败, 可能调用成功, 只是请求超时. 可以使用幂等接口设计应对超时失败.

合适的超时时间

假设有如下调用链.
a_node -5000ms> b_node -5000ms> outer_services
若outer_services, 即不可控的外部服务超时, 会导致a_node, b_node的call全部超时. 建议在b_node上对外使用较小的超时时间. 避免外部超时让整条调用链都出现超时错误.

阅读 1.6k

51 声望
3 粉丝
0 条评论
51 声望
3 粉丝
文章目录
宣传栏