如何接收发送到在 gen_server 内运行的 PID 的消息

Posted

技术标签:

【中文标题】如何接收发送到在 gen_server 内运行的 PID 的消息【英文标题】:How can I receive messages sent to a PID which running inside a gen_server 【发布时间】:2021-06-02 11:39:09 【问题描述】:

我有一个 ejabberd 服务器

我有一个自定义模块,my_apns_module.erl,它由 ejabberd 服务器运行,如下所示:

start_link(Host, Opts) ->
  Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
  ?GEN_SERVER:start_link(local, Proc, ?MODULE,
                         [Host, Opts], []).
start(Host, Opts) ->
  Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
  ChildSpec = Proc, ?MODULE, start_link, [Host, Opts],
               temporary, 1000, worker, [?MODULE],
  supervisor:start_child(ejabberd_sup, ChildSpec).

stop(Host) ->
  Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
  ?GEN_SERVER:call(Proc, stop),
  supervisor:delete_child(ejabberd_sup, Proc),
  ok.

init([Host, _Opts]) ->
   ...

handle_call(stop, _From, State) ->
  stop, normal, ok, State.

handle_cast(_Msg, State) -> noreply, State.

handle_info(#offline_msgus = _UserServer,
          from = From, to = To, packet = Packet = _Msg, State) ->
  ...

handle_info(_Info, State) ->
  noreply, State.

terminate(_Reason, State) ->
  Host = State#state.host,
  ok.

code_change(_OldVsn, State, _Extra) -> ok, State.

在 init 中,我运行另一个 APNs 应用程序来发送推送通知。 https://github.com/inaka/apns4erl

init([Host, _Opts]) ->
      apns:start(),
      case apns:connect(cert, ?APNS_CONNECTION) of
           ok, PID -> ?INFO_MSG("apns connection successful with PID ~p~n", [PID]);
           error, timeout -> ?ERROR_MSG("apns connection unsuccessful reason timed out", [])
      end,
      ok, #statehost = Host.

这是有效的,因为我可以发送通知。

现在文档 (https://github.com/inaka/apns4erl) 说:

如果网络出现故障或发生意外情况,枪 与 APN 的连接将断开。在这种情况下,apns4erl 将发送一个 向客户端进程发送消息reconnecting, ServerPid,这意味着 apns4erl 失去了连接,它正在尝试重新连接。一旦 连接已恢复 connection_up, ServerPid 消息将 发送。

我的问题是:

我应该在my_apns_module.erl 中写什么代码来接收reconnecting, ServerPidconnection_up, ServerPid

【问题讨论】:

【参考方案1】:

发送到 gen_server 进程的消息,例如GenServerPid ! ok, 10,由:

handle_info(Msg, State)

所以,你可以这样做:

handle_info(reconnecting, ServerPid=Msg, State) ->
     %%do something here, like log Msg or change State;
handl_info(connection_up, ServerPid=Msg, State) ->
     %%do something here, like log Msg or change State;   
handle_info(#offline_msgus = _UserServer,
          from = From, to = To, packet = Packet = _Msg, State) ->
     %%do something.

对评论的回应:

这是您当前的代码:

init([Host, _Opts]) ->
      apns:start(),
      case apns:connect(cert, ?APNS_CONNECTION) of
           ok, PID -> ?INFO_MSG("apns connection successful with PID ~p~n", [PID]);
           error, timeout -> ?ERROR_MSG("apns connection unsuccessful reason timed out", [])
      end,
      ok, #statehost = Host.

你可以把它改成这样:

init([Host, _Opts]) ->
    spawn(?MODULE, start_apns, []),
    ...


start_apns() ->

          apns:start(),
          case apns:connect(cert, ?APNS_CONNECTION) of
               ok, PID -> ?INFO_MSG("apns connection successful with PID ~p~n", [PID]);
               error, timeout -> ?ERROR_MSG("apns connection unsuccessful reason timed out", [])
          end,
          apns_loop().

apns_loop() ->
    receive
        reconnecting, ServerPid -> %%do something;
        connection_up, ServerPid -> %% do something;
        Other -> %% do something
    end,
    apns_loop().
  

启动apns进程后,apns进程会进入循环等待消息。

【讨论】:

等一下...消息不会发送到 gen_server,比如说 GenServerPID,而是发送到在 gen_server 中运行的进程,比如说 PID。该过程由语句 OK, PID = apns:connect(cert, ?APNS_CONNECTION) 启动。所以 GenServerPID 是由 ejabberd 启动的,而 PID 是通过 my_apns_module 中的 apns:connect 启动的。据我了解,handle_info 将处理 ejabberd 服务器发送到 GenServerPID 的消息。它还会处理发送到 PID 的消息吗? 据我所知,handle_info 将处理 ejabberd 服务器发送到 GenServerPID 的消息。它还会处理发送到 PID 的消息吗?-- 发送到非 gen_server 进程的消息需要由接收子句处理。 @GJain,我添加了一个你可以做什么的例子。 对不起......我还有一个问题...... PID)还是(是 spawnPID == PID)? GJain,哪个进程是PID? 在这种情况下,apns4erl 将向客户端进程发送一条消息 reconnecting, ServerPid -- 你必须弄清楚哪个进程是client process

以上是关于如何接收发送到在 gen_server 内运行的 PID 的消息的主要内容,如果未能解决你的问题,请参考以下文章

您如何在 gen_servers 中进行选择性接收?

如何在 erlang gen_server 中有效地使用接收子句来解决超时错误?

将异步任务发送到在其他线程中运行的循环

将事件处理程序附加到在循环内创建的表单

发送消息时确保 gen_fsm/gen_server 进程存在

erlang udp服务器无法接收接受数据包