OTP: gen_server的简单应用

1、确定回调模块名
2、编写接口函数
3、在回调模块里编写六个必需的回调函数

1、确定回调模块名
my_bank
2、编写接口方法
start() 打开银行
stop() 关闭银行
new_account(Who) 创建一个新账户
deposit(Who,Amount) 把钱存入银行
withdraw(Who,Amount) 把钱取出来(如果有结余的话)。

gen_server:start_link({local,Name},Mod,…) 会启动一个本地服务器。如果第一个参数是原子global,它就会启动一个能被Erlang节点集群访问的全局服务器。start_link的第二参数是Mod,也就是回调模块名。宏?MODULE会展开成模块名my_bank。
gen_server:call(?MODULE,Term)被用来对服务器进行远程过程调用。

3、编写回调方法
回调模块必需导出六个回调方法:
init/1,handle_call/3,handle_cast/2,handle_info/2,terminate/2,code_change/3.

	其中的handle_call/3函数最为最重要




这个模块包含了一套简单的框架,可以填充它们来制作服务器。如果忘记定义合适的回调函数,编译器就会根据关键字-behaviour来生成警告或错误消息。start_link()函数里的服务器名(宏?SERVER)需要进行定义,因为它默认是没有定义的。



这段代码中的Reply会作为远程过程调用的返回值发挥客户端。

State 只是一个代表服务器全局状态的变量,它会在服务器里到处传递。在上面的银行模块中,这个状态不会发生变化,只是一个ETS表的索引,属于常量。



启动服务器
gen_server:start_link(Name,Mod,InitArgs,Otps) 这个调用是所有事务的起点。

它会创建一个名为Name的通用服务器,回调模块是Mod,Otps则控制通用服务器的行为。在这里可以指定消息记录、函数调试和其他行为。通用服务器通过调用Mod:init(InitArgs)启动。

初始化服务器

init(Args) -> {ok,State}|
		{ok,State,Timeout}|
		ignore|
		{stop,Reson}
		
init([]) ->
{ok,#state{}}.

如果返回{ok,State},就说明我们成功启动了服务器,初始状态是State。

调用服务器
要调用服务器,客户端程序需要执行gen_server:call(Name,Request).他最终调用的回调模块里的handle_call/3.

handle_call(Request,From,State) ->
	{reply,Reply,State}|
	{reply,Reply,State,Timeout}|
	{noreply,State}|
	{noreply,State,Timeout}|
	{stop,Reason,Reply,State}|
	{stop,Reason,State}

Request(gen_server:call/2的第二个参数)作为handle_call/3的第一个参数重新出现。From是发送请求的客户端进程PID,State则是客户端的当前状态。

通常会返回{reply,Reply,NewState},Reply会返回客户端,成为gen_server:call的返回值。NewState则是服务器接下来的状态。

noreply会让服务器继续工作,但客户端会等待一个回复,所以服务器必需把回复的任务委派给其他进程。用适当的stop会停止服务器。



gen_server:call和handle_call之间的交互,作用是实现远程过程调用。

调用和播发
gen_server:cast(Name,Msg)则实现了一个播发(cast),也就是没有返回值得调用(实际上就是一个消息,但习惯上称它为播发来与远程过程调用相区分)。
handle_cast(Msg,State) -> 
	{noreply,State}|
	{noreply,State,Timeout}|
	{stop,Reason,State}
	

发给服务器的自发性消息
回调函数handle_info(Info,State)被用来处理发给服务器的自发性消息。
自发性消息是一切未经显示调用gen_server:call或gen_server:cast而到达服务器的消息。
例如:如果服务器连接到另一个进程并捕捉推出信号,就可能会突然收到一个预料之外的{‘EXIT’,Pid,What}消息。除此之外,系统里任何知道通用服务器PID的进程都可以向它发送消息。这样的消息在服务器里表现为info值。
handle_info(_Info,State) -> 
	{noreply,State}|
	{noreply,State,Timeout}|
	{stop,Reason,State}


后会有期,宝贝
服务器会因为许多原因而终止。某个以handle_开头的函数也许会返回一个{stop,Resason,NewState},服务器也可能崩溃并生成{‘EXIT',reason}。在所有这些情况下,无论他们是怎样发生的,都会调用terminate(Reason,NewState)。模板如下:
这个函数是在某个gen_server即将终止时调用的。
它应当是Module:init/1的逆操作,并进行必要的清理。
当他返回时,<mod>gen_server</mod>终止并生成原因Reason。

terminate(Reason,State) -> void().

这段代码不能返回一个新状态,因为已经终止。

但是了解服务器的终止状态非常有用。可以把状态保存到磁盘,把它放入消息发送给别的进程,或者根据应用程序的意愿丢弃它。如果想让服务期过后重启,就必须编写重启函数,由terminate/2触发。

代码更改
在服务器运行时更改它的状态。这个回调函数会在系统执行软件升级时由版本处理子系统调用。

在代码更改时转换进程状态
code_change(OldVsn,State,Extra) -> {ok,NewState}

image

参考引用:
Erlang程序设计(第二版)

posted on 2022-07-14 10:14  yunkuang  阅读(186)  评论(0编辑  收藏  举报