`
cnDenis
  • 浏览: 100450 次
  • 来自: 广州
社区版块
存档分类
最新评论

gen_server笔记

阅读更多

gen_server笔记

by cnDenis http://cndenis.iteye.com 2013年4月24日

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/1handle_call/3,handle_cast/2handle_info/2terminate/2code_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的文档

OTP设计原则中gen_server的介绍

分享到:
评论

相关推荐

    HPE Gen10重置BIOS方法1.pdf

    HPE MicroServer Gen10服务器的BIOS重置方法是一种比较特殊的硬件操作,通常情况下,大多数主板或笔记本电脑的BIOS重置过程可能仅仅需要用户在关机状态下按下特定的按钮几秒钟即可完成。但针对HPE MicroServer Gen10...

    sqlserver2019中文帮助(带书签,高清)

    文档提供了使用curl、SQL、Spark加载数据的方法,以及如何为HDFS分层装入数据到Azure Data Lake Gen2或S3。 【机器学习服务】支持Python和R,提供了快速入门教程,涵盖数据探索、建模、训练和评分。用户可以使用T-...

    erlang-kafka:Erlang 的 Kafka 消费者协议

    Kafka 协议客户端作为 OTP gen_server运行。 范围 描述 默认 笔记 主持人 要连接的 Kafka 代理的主机名 本地主机 主机名或 IP 地址作为元组 港口 Kafka 代理使用的端口号 9092 客户编号 发送给 Kafka 代理的客户端...

    Archlinux笔记

    10. **设置语言环境**:编辑`/etc/locale.gen`文件,启用需要的语言支持,如`en_US.UTF-8 UTF-8`,然后运行`locale-gen`生成语言环境配置。 11. **安装引导加载程序**: - **rEFInd**:使用`pacman -S refind-efi`...

    fdbus编译代码(涉及protobuf编译)-总结

    $ protoc ./common.base.Example.proto --cpp_out=./idl-gen ``` - 在客户端和服务端代码中包含`.pb.h`对应的头文件。 #### 五、编译fdbus示例 - 创建并进入example构建目录: ``` $ mkdir -p build-example/...

    CentOS7.2.1511 gcc4.8.5 通过编译的 tfs2.2.16

    centos7 tfs部署笔记.txt 环境信息: Docker version 1.8.2-fc22, build cb216be/1.8.2 Fedora release 22 (Twenty Two) Linux localhost.localdomain 4.0.4-301.fc22.x86_64 #1 SMP Thu May 21 13:10:33 UTC 2015 ...

    使用docxtpl生成巡检word(笔记留档用)

    这篇笔记将深入探讨如何使用`docxtpl`库来创建巡检Word文档,同时结合`BeautifulSoup`和`pandas`进行数据处理。 首先,我们需要了解`docxtpl`库的基本用法。`docxtpl`基于`python-docx`,通过提供模板语法,使得...

    linux学习笔记-初学者必备

    ### Linux学习笔记——初学者必备知识点 #### 一、源列表配置 在Linux系统中,尤其是Ubuntu这样的发行版,为了确保软件包的更新与安装,我们需要正确地配置系统的软件源。给定的内容显示了几个不同的软件源地址: ...

    usb3.0驱动

    USB3.0(也称为USB 3.1 Gen 1)是USB技术的一个版本,它的最大数据传输速率高达5Gbps,是USB2.0的十倍,极大地提高了数据传输效率。Asmedia是一家专注于USB接口解决方案的公司,其芯片广泛应用于各种主板和笔记本...

    grad_stats:GradCafe统计信息生成器

    研究生统计-轻松查询研究生录取历史数据 该站点可让您查询GradCafe数据并根据...$ streamlit run app/stats_gen.py --browser.serverPort 80 --browser.serverAddress gradstats.jjdv.xyz --server.port 8888 笔记 Gr

    learning-tornado-src:Tornado框架源码学习笔记

    tornado作为web框架和异步网络库,代码量过多,因此在分析tornado源码时,可以选择一些比较重要的模块来阅读,方式:current.py,gen.py,tcpserver.py,httpserver.py,ioloop .py,iostream.py,web.py等 ...

    events-app:活动策划网络应用

    了解更多官方网站: : 指南: : 文件: : 论坛: : 资料来源: : 笔记mix phx.gen.html Name Name name db_fields以创建新资源之后,需要在priv/repo/migrations/...编辑迁移文件然后将资源添加到路由器据我了解...

    grpc-tutorial:TUT-以下教程Edge gRPC教程

    生成器(这个支持插件): go install github.com/golang/protobuf/protoc-gen-go生成gRPC代码protoc --go_out=plugins=grpc:chat chat.proto跑# Run in separate terminalsgo run server.gogo run client....

    react-music-lhy:基于react16+ react-router4+ typescript react-redux的移动端音乐播放器

    一个适合练手的react项目 项目正在用hooks重构 体会vue与react项目区别 声明:本项目参考了滴滴大佬的vue项目,...可以通过dts-gen为没有types的npm包自动生成d.ts声明文件 项目笔记 D1 1、搭建项目 2、alias与tsconfig

    steal-riot-example:偷窃js + riotjs

    npm install live-server npm start 创建一个配置为0的http服务器 npm start启动并运行浏览器index.html 。 ##笔记 是从生成的: riot my-greet.tag gen 可以从package.json (npm)和bower.json两者中发现依赖...

Global site tag (gtag.js) - Google Analytics