Erlang网络通信
使用Erlang实现一个RPC服务器:
RPC表示远程过程调用(remote procedure call)。RPC服务器令你得以从远程机器上发起过程(也就是函数)调用。利用这个基于TCP的RPC服务器,人们只需要一个简单的TCP客户端(比如老式的Telnet)就能连上Erlang节点、执行Erlangt命令,并检查执行结果。要想对上线投产后的软件进行诊断,这个TCP RPC服务器会是一个良好的起点。
行为模式是面向进程编程中各种常见模式的一种形式化表述
OTP行为模式将这类反复出现的模式分成了两个部分:通用部分和具体应用相关的实现部分二者通过一套简单明确的接口进行通信。
行为模式的组成部分
- 行为模式接口;
- 行为模式实现;
- 行为模式容器。
下面详细来看:
-
行为模式的接口是一组特定的函数和相关的调用规范。gen_server行为模式的接口包含六
个函数:init/1、handle_ca11/3、handle_cast/2、handle_info/2、terminate/2和
code_change/3. -
实现,指的是由程序员提供的具体应用相关的代码。行为模式的实现是一个导出了接口
所需的全部函数的回调模块。实现模块中还应包含一项属性-behaviour(…),用以说明该模块所实现的行为模式的名称,这样编译器便可以协助检查模块是否完整地导出了接口所需的所有函数。 -
容器。容器是一个进程,它执行的是某个库模块中的代码,并且会调用与行为模式实现相对应的回调模块来处理应用相关的逻辑。(从技术角度说,容器也可以由多个密切相关的进程构成,但通常只有一个进程。)该库模块的名称与对应的行为
实例化
行为模式的目的在于为特定类型的进程提供一套模板。每个行为模式库中的模块都有一个或
多个用于启动新的容器进程的API函数(通常名为start和/或start_1ink)。我们将新容器进程
的启动称作行为模式的实例化。
标准行为模式实现模块中的源码包含以下几部分
- 首部
模块属性和样板内容 - API
编程接口;描述外界如何与模块交互 - 行为模式接口
行为模式接口所需的回调函数 - 内部函数
API和行为模式接口函数的辅助函数
模块命名:
标准的做法是给模块名加上一个恰当的前缀
模块首部
注释
注释用一个%就够了,但这里的每行注释都以3个%开头。这是文件级注释的规范
%%%
EDoc标签都以@字符开头
- @author
作者信息和邮箱地址 - @copyright
日期和版权归属 - @doc
常规文档文本。第一句是概要描述,其中可以包含XHTML和一些wiki标记 - @end
标志着上述任意一种标签的完结。此处可以防止一·这一行被
误识别为前面的@doc标签的内容
模块声明
第一项就是-module(…)属性。此处的名称必须与文件名对应
所要实现的是一个通用服务器,所以应该添加如下的行为模式属性:
%% 面向对象程序员可以把下面一行当做实现接口(实现gen_server)接口
-behaviour (gen_server).
接下来是导出声明。通常要写两个(编译器会将它们合而为一,分开来是为了提高可读性)。
第一个是你自已的API,第二个是行为模式接口要求导出的函数。
%%API
-export([]).
%%gen server callbacks
-export([init/1,handle_call/3,handle_cast/2,handle_info/2,
terminate/2,code_change/3]).
行为模式的接口函数常被称作回调(callback)。
另外还可以进行宏定义和记录声明如下:
-define(SERVER,?MODULE).
-define(DEFAULT PORT,1055).
-record(state,{port,Isock,request count 01).
API
模块的所有功能都是通过应用编程接口(API)提供给用户的。对于通用服务器而言,用户主要完成以下两件事:
- 启动服务器进程;
- 向进程发消息(并获取应答)。
gen_server提供了3个主要的库函数来实现这些基本功能。
库函数 | 对应的回调函数 | 描述 |
---|---|---|
gen_server:start_link/4 | Module:init/1 | 启动并链接一个gen server容器进程 |
gen_server:call/2 | Module:handle_call/3 | 向gen server进程发送同步消息并等待应答 |
gen_server:cast/2 | Module:handle_cast/2 | 向gen server进程发送异步消息 |
N/A | Module:handle_info/2 | 处理通过ca11或cast函数以外的手段发送给gen_server容器的消息。这些都是带外(out-of-band)消息消息抵达目的地之后,除非进程主动将消息取走,否则消息会一直停留在进程的信箱内。信箱的大小是没有上限的 |
gen_server:call/2函数实现了可靠的同步请求-响应功能,其中默认应答等待超时为5秒,一旦超时便放弃等待应答(另外一个版本gen_server:call/3允许以毫秒为单位设置超时,同时也可以通过将超时设置为infinity来
禁用超时)。
gen_server:start_link({local,?SERVER},?MODULE,[Port],[])
执行这个调用时,会派生出一个新的gen_server容器进程,新进程将以SERVER宏展开
后对应的名称在本地节点上注册,然后等待行为模式实现模块中的init/1回调函数完成
进程初始化,最后返回(更多相关内容参见3.2.4节)。至此,服务器启动完毕并完成了所
有初始化工作,已经准备好接受消息了。
第三个参数,即此处的【Port】,为服务器提供了启动用的初始数据。该参数会被传给
init/1回调函数,用于设置进程初始状态。第四个参数是附加参数列表,此处留空即可。
注意,从API用户的角度来看,这些细节全都被屏蔽了,用户只须关心一个参数:即服务器
的监听端口。
采用cal1或cast以外的手段发送给gen server:进程的所有消息都由
handle info/2回调函数处理。这些消息都被归类为带外消息,当你的服务器需要与第三方模
块通信,而第三方模块又依赖于直接消息通信而非OTP库调用(比如套接字或端口驱动)时,它就派得上用场了。不过如果可能的话,还是应该尽量避免使用带外消息。
在init/1函数中,你为服务器建立了一个TCP监听套接字,还从该函数中返回了一个神秘
的0超时值,从而立即触发超时(参见代码清单3-4)。
gen_server超时事件
gen_server设置了超时之后,一旦超时触发,就会产生一条由原子timeout构成的带
外消息,这条消息将由handle_call/2回调处理。该机制常用于处理服务器在超时时间内未
收到任何请求的情况,此时可以用它来唤醒服务器并执行一些指定操作。