【问题标题】:Why does gen_server get timeout为什么 gen_server 超时
【发布时间】:2020-08-20 09:44:32
【问题描述】:

我试图弄清楚为什么我的gen_server 会因超时而崩溃,因为我正在处理所有可能的情况:

module(wk).
-behaviour(gen_server).
-compile(export_all).


-record(state,{
    limit,
    count=0,
    toSend
}).
start_link(ToSend,Limit)->
   gen_server:start_link(?MODULE, {ToSend,Limit}, []).



init({ToSend,Limit})->
    State=#state{toSend=ToSend,limit=Limit},
    {ok,State}.


handle_call({process,Message},From,State)->
    {reply,{processed,os:timestamp()},State};
handle_call(Message,From,State)->
    self() ! {from_call,Message},
    {noreply,State}.
handle_cast(Message,State=#state{count=C})->
    self() ! {from_cast,Message},
    {noreply,State}.

handle_info(Message,State=#state{count=C,limit=L,toSend=T})->
    io:format("inside handle_info"),
    T! {badrequest,Message},
    Ret=if C>L -> {stop,State};
           _ ->{noreply,State#state{count=C+1}}
        end,
    Ret.

如您所见,此服务器可以处理许多 limit 未知消息,以及 cast 消息。 现在我的问题是handle_call:

  • 如果我发送一条符合第一种情况的消息,它可以并回复
  • 例如,当我使用 gen_server:call(S,xx) 发送未知消息时,我收到超时错误:

    异常退出:函数 gen_server:call/2 中的 {timeout,{gen_server,call,[,33]}}(gen_server.erl,第 215 行)

为什么我的服务器超时?我可以看到handle_call之后进入handle_info但是为什么会崩溃?

用法:

{ok,Y}=wk:start_link(self(),3).
 gen_server:cast(Y,some_message).  % works limit times and then crashes as expected
 Y ! some_message % works limit times and then crashes as expected
 gen_server:call(Y,some_message) % gets inside handle_info , since i get the io message, then times out

【问题讨论】:

    标签: erlang erlang-otp gen-server


    【解决方案1】:

    为什么我的服务器超时了?

    来自gen_server:call() 文档:

    call(ServerRef, Request) -> Reply
    call(ServerRef, Request, Timeout) -> Reply
    

    通过同步调用到 gen_server 进程的 ServerRef 发送请求并等待回复到达或超时 发生。
    ...
    ...

    Timeout 是一个大于零的整数,指定多少 等待回复的毫秒数,或无限等待的原子 无限期地。默认为 5000。如果在 指定时间,函数调用失败。

    因为您在handle_call() 函数中选择了返回{noreply...},所以gen_server:call() 无法返回并在5 秒后超时。您需要在超时之前手动调用gen_server:reply/2(在另一个进程中)向客户端发送回复。

    【讨论】:

    • 哦,所以基本上不是gen_server 超时了它的调用者。如果这是真的,为什么服务器在这之后不接受进一步的消息?为什么我不能向服务器发出其他调用?
    • 您调用了start_link() 来启动gen_server。名称“start_link”的意思是:启动服务器进程并将其链接到调用start_link() 的进程。并且,当两个进程链接时,如果其中一个进程死亡,则另一个进程将被杀死。如果您在 shell 中执行函数调用,您可以在启动 shell 后立即调用 self(). 以查看 shell 进程的 pid,然后在 Y 模式匹配后执行 Y. 以查看gen_server 进程....
    • ....然后在超时错误后,发出命令i().,你会看到这两个进程都不存在了。
    【解决方案2】:

    这里发生了一些事情。

    您正在从 GenServer 中向 self() 发送消息,该消息将被发送到 GenServer。这些消息需要在 handle_info 块中处理,我没有看到实现,所以此时,所有内容都被视为错误请求。

    其次,您正在通过您在状态中保存的 Pid/Name 将消息发送回呼叫者/施法者,这对于异步操作来说很好,只要您使用 {reply, "processing call" 之类的内容回复呼叫, State},因为你使用了 noreply,你的电话已经超时了。通常,您不希望使用立即返回某些内容的调用,但如果您需要确认该流程已获得工作并正在处理它,那么它可能是一种很好的责任移交机制。否则,如果它是一个同步调用,请使用 gen_server:reply 而不是 T !消息语法,但您必须以不同的方式处理调用和强制转换。

    祝你好运

    【讨论】:

    • handle_info 块,我没有看到实现, -- 转到代码的最后一行并向上阅读。
    • 我看到了handle_info,但看不到您发送的特定消息,除非您想以同样的方式对待所有内容,即视为错误。
    【解决方案3】:

    是客户端超时。一旦它调用 gen_server:call/2 的第二个子句,客户端仍在等待响应,该响应永远不会被发回。

    【讨论】:

      猜你喜欢
      • 2016-06-28
      • 2014-05-12
      • 2021-07-11
      • 2021-03-19
      • 2018-04-22
      • 2017-06-02
      • 2020-09-02
      • 2021-11-06
      • 2017-10-30
      相关资源
      最近更新 更多