如何直观地描述 gen_server?

Posted

技术标签:

【中文标题】如何直观地描述 gen_server?【英文标题】:How to describe gen_server visually? 【发布时间】:2011-10-24 19:35:31 【问题描述】:

免责声明:作者是 OTP 的新手,对 Erlang 的语法、流程和消息有一些基本的了解。

我试图掌握 Erlang 中行为的概念,但我脑海中浮现出许多问题,使我无法理解像 gen_server 这样的行为的全部原理。

好的,gen_server 的官方文档显示了一个服务器和三个客户端连接的漂亮图表,查询和回复箭头: http://www.erlang.org/doc/design_principles/gen_server_concepts.html

但每次我试图进一步理解这个概念时,我都会陷入困境。

有很多概念我无法在脑海中构建成一个更大的概念:

行为实现; 行为容器; 行为界面; 回调模块; 回调函数; API 函数。

我使用以下资源:

Erlang/OTP 实践手册; OTP 行为介绍简介,http://www.slideshare.net/gamlidek/ceug-introduction-to-otp-behaviors-part-i-genserver; “ErlyBank”http://spawnlink.com/articles/an-introduction-to-gen_server-erlybank/index.html。

我仍然处于“我们在一个模块中调用一个函数,这个函数调用另一个函数,那个函数创建一个进程......卡住”的状态

有什么方法可以用图表来描述 gen_server 的概念吗?如何直观地显示客户端和服务器之间的交互流程?(帮助不太聪明的新手直观地理解概念)

比如这里:http://support.novell.com/techcenter/articles/img/dnd2003080506.gif

UPD:我试过自己画一个图,但还是没搞明白图中任何连接器的用途:http://postimage.org/image/qe215ric/full/

UPD2:这与我希望看到的类似:http://cryptoanarchy.org/wiki/Worker_patterns(模型)。但是,它没有显示模块、函数和进程之间的交互。

【问题讨论】:

Martin,可能会混淆单例 gen_server 的注册的事情之一。我在大多数教程中将其视为对 gen_server 的第一次介绍,如果您试图掌握 gen_server,就会感到非常困惑。因此, start_link 中的 local, CH3 使其成为单例 -> 您正在查看的 gen_server 模块只有一个进程。 @WardB 谢谢,我会考虑到这一点。 哇!!!你根本没有提到阿姆斯特朗的书……实用的编程!你必须经历它。它有一个非常简单且逐步的 OTP 指南 @AruMu 我写信给 LYSE 的作者,你的建议真的很有用,谢谢! 【参考方案1】:

我没有精确的图纸来解释它,但我有 this chapter 和展示了如何从其背后的抽象原理开始构建 gen_server 之后的那个。

帮助处理各个组件:

行为实现

行为本身有点像我之前链接的章节中显示的内容。它是一个具有一堆功能的模块,为您完成所有通用工作:接收消息、定义功能和隐藏的通信协议等。高级 OTP 内容包含用于进行软件升级的特殊类型的消息以及用于跟踪选项的特殊代码。

行为容器

我不确定这应该是什么。也许只是带有行为名称的模块?

行为界面

在您的行为实现所在的同一模块中,您必须定义一个behaviour_info/1 函数。该函数将使 Erlang 编译器知道任何包含 -behaviour(SomeModuleName) 的模块都需要一些回调。 SomeModuleName 等效于包含实现和 behavior_info 函数的 SomeModuleName.erl(和 .beam)文件。

回调模块

将包含所有特定代码的模块,处理所有自定义内容。

回调函数

所有非通用的都以YourModule:SomeCall(Args) 的形式委托给回调模块。这些由您的模块提供,其中包含-behaviour(gen_server). 行。

API 函数

如果需要,回调模块有两个接口:一个用于gen_server 行为(init/0、handle_call/3、handle_info/2、handle_cast/2、terminate/2、code_change/3),以及一个给用户(启动服务器,发送一些信息,请求一些信息回来)。

我可以试着这样描述它

---------------------------------------------------------------------
| some process          |                server process             |
------------------------+--------------------------------------------
   [client]             |      [callback]     :        [behaviour]
                        |                     :
 callback:start >-------|---------------------:--> starting the process
                        |                     :           V
                        |                     :           |
                        |       init()  <-----:-----------`
                        |         |           :
                        |         `-----------:------> initial state
  ok, Pid  <----------|---------------------:----------,/
                        |                     :
 callback:store  >------|---------------------:--> handles message
 (calls the process)    |    (formats msg)    :           V
                        |                     :           |
                        |    handle_call() <--:-----------` 
                        |         |           :
                        |          `----------:--> updates state, sends reply
                        |                     :        V
                        |                     :        |
   gets result <--------|---------------------:--------`
                        |                     :       

所有通用部分都在服务器进程的右侧,在行为内,所有特定部分都在左侧(回调)。客户端使用回调模块的API/接口联系服务器进程并对其产生影响。

您必须将行为视为某种非常通用的代码段,有时会将其执行流程(对于更精确的部分,例如接收和发送消息)放弃给特定代码(如何对这些消息作出反应)。

希望这会有所帮助。

【讨论】:

很好的答案,谢谢!你的照片和阅读阿姆斯特朗的书的建议帮助我理解了这个概念。顺便说一句,你认为像“Erlang/OTP in Action”所建议的那样,只用非常基本的 Erlang 本身知识来学习 OTP 会更好吗? (第 92 页:“您不需要任何关于模块、流程、功能或消息传递的先验知识即可掌握......” 我不同意,不。然而,与其他人相比,这可能取决于我的学习方式。我喜欢框架,因为我能理解它们为什么会这样,以及它们解决的问题不是“让事情变得更容易”。我想知道 OTP 为我做了什么以及它是根据什么原则构建的。我发现它有助于我尊重最初的想法,并使我更容易扩展它。你会看到这就是我在 LYSE 中展示事物的方式:展示我们为什么需要抽象,然后展示如何粗略地构建抽象,然后展示框架。 对我来说,其他人有不同的学习方式对他们来说更自然,这完全有道理。

以上是关于如何直观地描述 gen_server?的主要内容,如果未能解决你的问题,请参考以下文章

在这种情况下我应该使用哪种 Erlang 行为,即 gen_server 或 gen_fsm

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

如何停止在erlang中作为gen_server实现的tcp_listener

Erlang:如何在主管中正确调度带有 start_child 的 gen_server 并调用 API

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

Erlang gen_server:如何捕获错误?