4

我正在寻找使用 gen_server:cast/2 处理Erlang异步消息的好例子。

我在OTP ssh模块中看到过一个例子,它通过Module:handle_cast/2接收请求,将其保存在模块的本地队列中,稍后通过显式发送消息发送回与请求对应的回复消息给来电者。当我试图阅读它时,我几乎无法跟踪代码,也无法掌握这个想法。

一段伪代码表示赞赏。

4

2 回答 2

5

我相信您指的是ssh_connection_manager模块。

当您执行 agen_server:cast/2时,请求将在Module:handle_cast/2函数中处理。这里有几点需要注意:

  • handle_cast参数中,您没有关于发件人的信息,因此您不能 - 除非您在消息本身中发送此信息 - 将一些结果发回给它。
  • 客户端发出 a 后gen_server:cast/2,不会等待回复。实际上,它甚至不关心消息是否到达(有一些例外)。
  • 在 anhandle_cast/2中,您可以只返回一个noreply或一个停止元组,因此无法在那里返回回复。

也就是说,您一直在查看的代码背后的想法应该是(简化事情):

  • 进行初始同步 gen_server:call/2
  • 客户端的From被传递到服务器并保存到其状态(实际上,似乎使用该参数创建了一个额外的进程)。当您无法在 handle_call 时计算返回值时,此技巧很有用。
  • 此时,您有两种可能性,具体取决于您是否需要更多信息来计算来自其他客户端 (A) 或来自同一客户端 (B) 的结果:

    • A. 在 handle_call 中返回类似 {noreply NewState} 的内容。这将使您能够在客户端仍然持有时处理其他请求。当结果准备好后,您可以使用 gen_server:reply() 将其发送回客户端。
    • B. 将 {reply, ok, State} 返回给客户端。客户端将继续执行,可能会执行一系列 cast/2。然后,客户会用新的gen_server:call/2.
于 2011-06-14T23:56:36.703 回答
4

通常,您在发送演员表时不会期望直接回复,否则您将使用 gen_server:call。

一个真实的例子,我有一个 gen_server 处理一些“通道”,并且有很多用途可以将通道名称附加到错误日志中。通道名称存储在 gen_server 的状态中。为了不阻止想要记录错误的进程,我不使用“获取名称”同步调用来获取名称作为前缀,而是使用强制类型转换发送消息:

error(Pid, Tags) ->
    gen_server:cast(Pid, {log, error_report, Tags}).

warning(Pid, Tags) ->
    gen_server:cast(Pid, {log, warning_report, Tags}).

info(Pid, Tags) ->
    gen_server:cast(Pid, {log, info_report, Tags}).

演员表在一个非常简单的处理程序中处理,不会返回。

handle_cast({log, Report, Tags}, #state{name=Name}=State) ->
    error_logger:Report([{chan, Name} | Tags]),
    {noreply, State};

如果您有异步消息要发回,这完全独立于演员表的处理。您不知何故需要知道将这些消息发送到哪里,您必须以State某种方式存储这些消息,或者您使用的是固定名称。

通常,您不应该只发送消息,而是调用接收进程模块的函数(这可能是另一个强制转换或普通消息发送)。

于 2011-06-14T06:52:30.407 回答