调用关系:
通用服务器模块(gen_server) 回调模块(my_bank)
gen_server:start_link ------------> Module:init/1 gen_server:call gen_server:multi_call ------------> Module:handle_call/3
gen_server:cast
gen_server_abcast ------------> Module:handle_cast/2
-- ------------> Module:handle_info/2
-- ------------> Module:terminate/2
-- ------------> Module:code_change/3
当回调函数调用失败或返回错误值时,gen_server会中止。 gen_server不会自动跟踪退出信号。
gen_server行为模式的基本流程如下:
回调模块的start调用gen_server:start_link(或gen_server:start)来启动服务器,并进入主循环,在回调模块的接口函数(处理具体的业务逻辑)中调用gen_server的call或cast来处理请求,在gen_server的call和cast中进一步调用回调模块中的handle_call和handle_cast来执行具体的业务逻辑处理,回调模块的terminate来终止进程。
gen_server:start_link:
start()函数对应的回调函数是init/1
,一般来说是进行服务器启动后的一些初始化的工作, 并生成初始的状态State,正常返回是{ok, State}。这个State是贯穿整个服务器, 并把所有六个回调函数联系起来的纽带。它的值最初由init/1
生成, 此后可以由三个handle函数修改,每次修改后又要放回返回值中, 供下一个被调用的handle函数使用。 如果init/1
返回ignore
或{stop, Reason}
,则会中止服务器的启动。有一点细节要注意的是,API函数和回调函数虽然习惯上是写在同一个文件中,但执行函数 的进程却通常是不一样的。在上面的模板中,start_link/0
中使用self()
的话,显示的是调用者的进程号,而在init/1
中使用self()
的话,显示的是服务器的进程号。
gen_server:multi_call:
call对应的回调函数handle_call/3
在正常情况下的返回值是{reply,Reply,NewState}
, Reply
会作为call的返回值传递回去,NewState
则会作为服务器的状态。 另外还可以使用{stop, Reason, State}
中止服务器运行,这比较少用。
使用call要小心的是,两个服务器进程不能互相call,不然可能会出现死锁。
gen_server:cast:
cast是没有返回值的调用,一般把它叫做通知。它是一个“异步”的调用,调用后会直接收到 ok
,无需等待回调函数执行完毕。它的形式是gen_server:cast(ServerRef, Request)
。参数含义 与call相同。由于不需要等待返回,所以没必要设置超时,没有第三个参数。在多节点的情况下,可以用abcast
,向各节点上的具有指定名字的服务进程发通知。
cast们对应的回调函数是handle_cast/2
,具体为:handle_cast(Msg, State)
。 第一个参数是由cast传进去的,第二个是服务器状态,和call类似。handel_cast/2
的返回值通常是{noreply, NewState}
,这可以用来改变服务器状态, 或是{stop, Reason, NewState}
,这会停止服务器。通常来说,停止服务器的命令用 cast来实现比较多。
原生消息
原生消息是指不通过call或cast,直接发往服务器进程的消息。 比方说别的进程用!发过来的消息、跟服务器建立链接的进程死掉了, 发来{'EXIT', Pid, Why}
等等。一般写程序要尽量用API,不要直接用!向服务器进程发消息, 但对于socket一类的依赖于消息的应用,就不得不处理原生消息了。
原生消息使用handle_info/2
处理,具体为handle_info(Info, State)
。其中Info是 发过来的消息的内容。回复和handle_cast
是一样的。