Erlang --- gen_server

行为模式

gen_server代表的就是“行为模式”的一种,行为模式的目的在于为特定类型的进程提供一套模板。

启动服务器

用来启动服务器的有start/3,start/4,start_link/3,start_link/4这四个函数。 使用这些start函数之后,就会产生一个新的进程,也就是一个gen_server服务器。这些 start函数的正常情况下返回值是{ok,Pid}Pid就是这个新进程的进程号。 带link与不带的区别在于是否跟父进程建立链接,换种说法是,新启动的进程死掉后,会不会通知启动他的进程(父进程)。

start函数可以四个参数(ServerName, Module, Args, Options)

  • 第一个参数ServerName是服务名, 是可以省掉的。具有相同服务名的模块在一个节点中只能启动一次,重复启动会报错,为 {error, {already_started, Pid}}。具有服务名的服务进程可以使用服务名来调用, 没有服务名的只能通过进程号pid来调用了。通常有名字的服务进程会使用模块名做为 服务名,即上面模板中定义的宏-define(SERVER, ?MODULE),然后在需要使用服务名的 地方填入?SERVER.
  • 第二个参数Module是模块名,一般而言API和回调函数是写在同一个文件里的,所以就用 ?MODULE,表示本模块的模块名。
  • 第三个参数Args是回调函数init/1的参数,会原封不动地传给init/1
  • 第四个参数Options是一些选项,可以设置debug、超时等东西。

start对应的回调函数是init/1,一般来说是进行服务器启动后的一些初始化的工作, 并生成初始的状态State,正常返回是{ok, State}。这个State是贯穿整个服务器, 并把所有六个回调函数联系起来的纽带。它的值最初由init/1生成, 此后可以由三个handle函数修改,每次修改后又要放回返回值中, 供下一个被调用的handle函数使用。 如果init/1返回ignore{stop, Reason},则会中止服务器的启动。

有一点细节要注意的是,API函数和回调函数虽然习惯上是写在同一个文件中,但执行函数 的进程却通常是不一样的。在上面的模板中,start_link/0中使用self()的话,显示的是调用者的进程号,而在init/1中使用self()的话,显示的是服务器的进程号。

使用服务器

三个handle开头的回调函数对应着三种不同的使用服务器的方式。如下:

gen_server:call     -------------   handle_call/3
gen_server:cast     -------------   handle_cast/2
用!向服务进程发消息   -------------   handle_info/2

call是有返回值的调用;cast是无返回值的调用,即通知;而直接向服务器进程发的 消息则由handle_info处理。

call是有返回值的调用,也是所谓的同步调用,进程会在调用后一直等待直到回调函数返回为止。 它的函数形式是 gen_server:call(ServerRef, Request, Timeout) -> Reply

  • 第一个参数ServerRef是被调用的服务器,可以是服务器名,或是服务器的pid。
  • 第二个参数Request会直接传给回调函数handle_call。
  • 最后一个参数Timeout是超时,是可以省略的,默认值是5秒。

call是用来指挥回调函数handle_call/3干活的。具体形式为 handle_call(Request, From, State)

  • 第一个参数Request是由call传进来的,是写程序时关注和处理的重点。
  • 第二个参数From是gen_server传进来的,是调用的来源,也就是哪个进程执行了call。 From的形式是{Pid, Ref}Pid是来源进程号,而Ref是调用的标识,每一次调用 都不一样,用以区别。有了Pid,在需要向来源进程发送消息时就可以使用,但由于call 是有返回值的,一般使用返回值传递数据就好。
  • 第三个参数State是服务器的状态,这是由init或是其他的handle函数生成的,可以根据需要进 行修改之后,再放回返回值中。

call

call对应的回调函数handle_call/3在正常情况下的返回值是{reply, Reply, NewState}, Reply会作为call的返回值传递回去,NewState则会作为服务器的状态。 另外还可以使用{stop, Reason, State}中止服务器运行,这比较少用。

使用call要小心的是,两个服务器进程不能互相call,不然会死锁。

cast

cast是没有返回值的调用,一般把它叫做通知。它是一个“异步”的调用,调用后会直接收到 ok,无需等待回调函数执行完毕。

它的形式是gen_server:cast(ServerRef, Request)。参数含义 与call相同。由于不需要等待返回,所以没必要设置超时,没有第三个参数。

在多节点的情况下,可以用abcast,向各节点上的具有指定名字的服务进程发通知。 (奇怪的是为啥为不叫multi_cast,明明长得跟multi_call很像的)

cast们对应的回调函数是handle_cast/2,具体为:handle_cast(Msg, State)。 第一个参数是由cast传进去的,第二个是服务器状态,和call类似,不多说。

handel_cast/2的返回值通常是{noreply, NewState},这可以用来改变服务器状态, 或是{stop, Reason, NewState},这会停止服务器。通常来说,停止服务器的命令用 cast来实现比较多。

原生消息

原生消息是指不通过call或cast,直接发往服务器进程的消息,有些书上译成“带外消息”。 比方说网络套接字socket发来的消息、别的进程用!发过来的消息、跟服务器建立链接的进程死掉了, 发来{‘EXIT‘, Pid, Why}等等。一般写程序要尽量用API,不要直接用!向服务器进程发消息, 但对于socket一类的依赖于消息的应用,就不得不处理原生消息了。

原生消息使用handle_info/2处理,具体为handle_info(Info, State)。其中Info是 发过来的消息的内容。回复和handle_cast是一样的。

停止服务器

上面介绍的handle函数返回{stop,...},就会使用回调函数terminate/2进行扫尾工作。 典型的如关闭已打开的资源、文件、网络连接,打log做记录,通知别的进程“我要死啦”, 或是“信春哥,满血复活”:利用传进来的状态State重新启动服务器。

最简单的就是啥都不干,返回ok就好了。

时间: 2024-10-05 05:22:49

Erlang --- gen_server的相关文章

[Erl_Question09]Erlang gen_server实现定时器(interval)的几种方法及各自的优缺点?

position:static(静态定位) 当position属性定义为static时,可以将元素定义为静态位置,所谓静态位置就是各个元素在HTML文档流中应有的位置 podisition定位问题.所以当没有定义position属性时,并不说明该元素没有自己的位置,它会遵循默认显示为静态位置,在静态定位状态下无法通过坐标值(top,left,right,bottom)来改变它的位置. position:absolute(绝对定位) 当position属性定义为absolute时,元素会脱离文档流

Erlang generic standard behaviours -- gen_server system msg

这是Erlang generic standard behaviors gen_server 分析的系列的最后一篇,主要分析gen_server module 辅助性的功能函数. 在gen_server 的MAIN loop 流程中,除了处理Parent 的'EXIT' 消息, user module 常规消息之外, 还处理了一类 system 消息. 这一类system 消息的来源是一个sys 的module,在Erlang OTP体系中,sys module 主要有两大类的作用,一个是热更,

Erlang点滴--杀死gen_server

前天同事碰到了一个问题:他为游戏写了一个模拟客户端的机器人程序,用的是gen_server行为.但是他启动这些机器人时并没有通过监控树,而是直接在Shell下启动了若干个.然后他就发现如果其中一个机器人进程挂掉的话,所有的机器人都会跟着挂掉. 当他把问题告诉我时我第一反应就是Shell挂掉了,因为所有的机器人都是在Shell下用start_link启动的,也就是说所有的机器人进程都和Shell进程建立了连接.因此如果其中一个机器人挂掉,它会向Shell发送EXIT消息导致Shell挂掉,Shel

erlang中启动一个gen_server和一个纯粹进程的区别

1.在genserver的启动过程中,内部调用的是proc_lib:start_link(M,F,A,Time,SpawnOpts),当该函数被调用的时候,启动gen_server的进程必须wait,直到gen_server进程在执行init_it函数时,调用proc_lib:init_ack将gen_server的进程号返回给启动进程,至此一个gen_server进程才启动完成: 2.对于一个单纯的erlang进程来说,调用spawn函数来启动他,会立刻返回.

Erlang generic standard behaviours -- gen_server noblock call

在Erlang 系统中,经常需要gen_server 进程来处理共享性的数据,也就是总希望一个gen_server 进程来为多个普通进程提供某种通用性的服务,这也是gen_server 设计的初衷.但是,由于公平调度的原因,在Erlang体系中,每个process 能获得的资源都是同等的:同等的CPU时间片(还有默认情况下同等的初始化内存). 也就是gen_server 进程只能获得1/(N+1)的CPU时间片,为N个进程提供通用性的服务,而无法违背公平调度的原则使gen_server 进程获得

erlang使用gen_server实现质数服务器(手打代码,还debug了几个错误)

最近简单了解一下erlang语言,操练了一下书里的例子,有机会debug:下面是主要模块 prime_server,使用gen_server作为behaviour: 1 -module(prime_server). 2 -export([new_prime/1, start_link/0]). 3 4 -behaviour(gen_server). 5 -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/

Erlang OTP学习(1)gen_server

在<Programming Erlang>的OTP introduction章节中,作者通过循序渐进的方式,向我们展示了gen_server设计思路,现在做下总结: 在具体看gen_server之前,我们先看一个server通用框架:  在这个server里,你几乎看不到任何和具体功能相关的东西,它只提供了一个server所具备的基本框架,那它是如何运行的呢? 当我们调用start函数时,就启动了一个服务,如果服务器接收到一条消息,那么它将会把这条消息转交给Mod:handle_call或者M

[Erlang之旅 0004] gen_server

gen_server:start_link(ServerName, Module, Args, Options) -> Result ServerName={local, Name}|{global, GlobalName}|{via, Module, ViaName} Option = {debug, Dbgs}|{timeout, Time}|{spawn ——opt, Sopts} Module: 回调模块的名称 Args: 传给回调模块中init函数的参数 call(ServerRef,

Erlang入门:gen_server实例操作与练习2

Eshell V5.10.4 (abort with ^G) 1> bank_server3:create_account(name1, 1000). true 2> name1 ! check. Current money is: 1000 check 3> erlang:send(name1, check). Current money is: 1000 check 4> erlang:send(name1, abcd). handle_info: abcd abcd Year