gen_server笔记
http://www.ask3.cn/a/jingcaibowen/tech/Erlang/2013/0614/42043.html
gen_server是erlang的OTP框架中最常用的“行为模式”了吧,至少几本erlang教材都是首 先介绍这个。
这东西用来做什么的呢?或者说,为什么要用这东西呢?由于我接触这东西不过几天, 理解尚非常粗浅,就我看来,用gen_server有以下几个好处:
面向对像,封装数据与方法。gen_server内部需要维护一个状态State,并提供各种函数 给别人调用,就类似于其他语言中的“类”一样,把数据和方法封装在一起,防止数据被非法 改动。
简化调用,屏蔽通讯。erlang中如果要自己实现远程过程调用(RPC)的话,需要自己 定义消息格式,自己编写封装与解包的代码,还要处理各种异常问题,这些gen_server 都帮我们做好了,只需要像平常使用函数一样直接调就用行了,省时省力还不出错。
热代码替换等高级功能。 热代码替换是erlang大力宣传的一项特色功能,不停机维护 在生产上是非常美妙的事。不过这是高级功能,初学暂时用不上,书上也没多讲。
gen_server模板
gen_server是可以使用模板来写的,如下:
-module().
-behaviour(gen_server).
%% API
-export([]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-define(SERVER,?MODULE).
start_link() ->
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
init([]) ->
{ok, State}.
handle_call(_Request, _From, State) ->
{reply, Reply, State}.
handle_cast(_Msg, State) ->
{noreply, State}.
handle_info(_Info, State) ->
{noreply, State}.
terminate(_Reason, _State) ->
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
含义如下:
-module 是模块名,就是文件名。
-behaviour 是指定“行为模式”,在这里就是gen_server,作用在于用于检查这个模块是否 实现gen_server的所有接口。也就是 init/1, handle_call/3,handle_cast/2, handle_info/2, terminate/2, code_change/3这六个函数。缺少的话编译器就会 报错。
-export 有两个,第一个里面写的是API,也就是供别人调用的接口,第二个是gen_server 的接口,就是上面说的那六个。其实要写到同一个export里也可以,不过分开写比较清楚。
接下来就是各API函数,一般来说就是封装、调用各用回调函数的包装函数。
再往后就是六个回调函数的具体实现。回调函数是负责具体干活的。
大概可以做这么个比喻吧,如果整个模块是个昆虫,API是就头,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
call是有返回值的调用,也是所谓的同步调用,进程会在调用后一直等待直到回调函数返回为止。 它的函数形式是 gen_server:call(ServerRef, Request, Timeout) -> Reply,
第一个参数ServerRef是被调用的服务器,如果是服务器名,或是服务器的pid。
第二个参数Request会直接传给回调函数handle_call
最后一个参数Timeout是超时,是可以省略的,默认值是5秒。
在多节点的情况下,还有机会使用到multi_call,用来向各节点上的同具有相同注册名 的服务进程发起调用。(这个函数在文档上的表述有点让人难以理解,详见 这里)
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对应的回调函数handle_call/3在正常情况下的返回值是{reply, Reply, NewState}, Reply会作为call的返回值传递回去,NewState则会作为服务器的状态。 另外还可以使用{stop, Reasion, 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就好了。
gen_server笔记的更多相关文章
- Gen_server行为分析与实践
1.简介 Gen_server实现了通用服务器client_server原理,几个不同的客户端去分享服务端管理的资源(如图),gen_server提供标准的接口函数和包含追踪功能以及错误报告来实现通用 ...
- Erlang 学习笔记
http://wenku.baidu.com/link?url=AUQR8Hn-e-fEB_lqjXsd8XfapWj1qAK7J05JoBXFib_LlSk5qSOTia8HIxNV1XkeZi-k ...
- git-简单流程(学习笔记)
这是阅读廖雪峰的官方网站的笔记,用于自己以后回看 1.进入项目文件夹 初始化一个Git仓库,使用git init命令. 添加文件到Git仓库,分两步: 第一步,使用命令git add <file ...
- js学习笔记:webpack基础入门(一)
之前听说过webpack,今天想正式的接触一下,先跟着webpack的官方用户指南走: 在这里有: 如何安装webpack 如何使用webpack 如何使用loader 如何使用webpack的开发者 ...
- SQL Server技术内幕笔记合集
SQL Server技术内幕笔记合集 发这一篇文章主要是方便大家找到我的笔记入口,方便大家o(∩_∩)o Microsoft SQL Server 6.5 技术内幕 笔记http://www.cnbl ...
- PHP-自定义模板-学习笔记
1. 开始 这几天,看了李炎恢老师的<PHP第二季度视频>中的“章节7:创建TPL自定义模板”,做一个学习笔记,通过绘制架构图.UML类图和思维导图,来对加深理解. 2. 整体架构图 ...
- PHP-会员登录与注册例子解析-学习笔记
1.开始 最近开始学习李炎恢老师的<PHP第二季度视频>中的“章节5:使用OOP注册会员”,做一个学习笔记,通过绘制基本页面流程和UML类图,来对加深理解. 2.基本页面流程 3.通过UM ...
- NET Core-学习笔记(三)
这里将要和大家分享的是学习总结第三篇:首先感慨一下这周跟随netcore官网学习是遇到的一些问题: a.官网的英文版教程使用的部分nuget包和我当时安装的最新包版本不一致,所以没法按照教材上给出的列 ...
- springMVC学习笔记--知识点总结1
以下是学习springmvc框架时的笔记整理: 结果跳转方式 1.设置ModelAndView,根据view的名称,和视图渲染器跳转到指定的页面. 比如jsp的视图渲染器是如下配置的: <!-- ...
随机推荐
- FreeNX
freenx 在Linux下,我们最常使用的远程管理工具是ssh客户端,比如putty.SecureCRT等,但是ssh只提供字符界面的操作方式,有时我们需要图形界面的操作,在Linux下支持图形界面 ...
- 如何去掉bootstrap table中表格样式中横线竖线
修改之前,表格看上去比较拥挤,因为bootstrap table插件中自带斑马线表格样式,有横线和竖线分栏,现在我们不需要这些. 应UI设计的要求,要去掉中间的横线和竖线,使用了修改需求中一种简单粗暴 ...
- jvm compile
>>>Making sec-files-win @ Thu Oct 17 20:34:02 CST 2013 ... >>>Making jgss-files @ ...
- Php无限层级并显示层级数
今天在处理递归无限层级菜单时,遇到一个稍微烧脑的问题,如何显示当前节点所在的层级数.废话不多说,我们先看个直观的无限层级: <?php // 这里的arr是直接从数据库取出的,仅作为测试数据 $ ...
- 洛谷—— P1017 进制转换
https://www.luogu.org/problem/show?pid=1017#sub 题目描述 我们可以用这样的方式来表示一个十进制数: 将每个阿拉伯数字乘以一个以该数字所处位置的(值减1) ...
- RMQ问题-ST方法
参考 http://blog.csdn.net/sdj222555/article/details/7875575 RMQ 就是 Range Minimum/Maximum Query 就是求区间最值 ...
- [Angular 2] BYPASSING PROVIDERS IN ANGULAR 2
Artical --> BYPASSING PROVIDERS IN ANGULAR 2 Here trying to solve one problem: On the left hand s ...
- [Ramda] Declaratively Map Predicates to Object Properties Using Ramda where
Sometimes you need to filter an array of objects or perform other conditional logic based on a combi ...
- hadoop配置文件的加载机制 分类: A1_HADOOP 2015-01-21 11:29 839人阅读 评论(0) 收藏
hadoop通过Configuration类来保存配置信息 1.通过Configuration.addResource()来加载配置文件 2.通过Configuration.get***()来获取配置 ...
- Opencv分水岭算法——watershed自动图像分割用法
分水岭算法是一种图像区域分割法,在分割的过程中,它会把跟临近像素间的相似性作为重要的参考依据,从而将在空间位置上相近并且灰度值相近的像素点互相连接起来构成一个封闭的轮廓,封闭性是分水岭算法的一个重要特 ...