OTP gen_server入门

初识OTP

OTP,即电信开放平台,第一次见这个词还以为与电信通信相关,有一定的误导性,但在学习了解之后才明白OTP其实是一个通用性非常强的应用程序操作系统。
通过对OTP的了解,其gen_server模块非常像我早期学习linux服务器开发时了解的一款开源网络框架lib_event(代表软件NetChat),不过在踏上这条崭新的道路之后,依然需要从最简单的做起,去了解背后具体的构建逻辑,本文旨在分享笔者在学习gen_server过程中所做的代码实践,以及分享遇到的一些问题以及笔者在学习过程中的一些思路和感谢。

通用服务器之路——搭建最基本的服务器

想一想曾经我所写过的lib_event服务器,其最主要的业务是处理用户的地理信息,接受用户请求,经过服务器处理数据,发送给数据库服务器,返回结果给用户,从中我们能抽象出一个服务器最最基本的要素——收发信息业务处理以及启动和停止
现在我们用erlang来编写一个最最简单的服务器,他的功能是存储用户上一次传入的位置
general_server
如你所见 这是一个服务器最最简单的结构,
start:负责启动一个注册名为Name的进程,这个进程拉起一个loop服务,但是目前启动的服务器还没有业务功能,需要我们传入一个Mod给这个服务器添加业务,这意味着我们不需要了解服务器的构造与细节,不需要改变loop,仅仅只是传入回调模块即可实现业务以及拓展功能(OTP简直是天才设计)。
rpc:远程过程调用,因为我们实现业务的代码其实不在本服务器上(本服务器只是一个架构),所以我们需要通过远程调用,去调用真正执行业务的服务器!
loop:服务器的本体,一个循环执行的函数,其功能是持续监听发给本服务器的请求,处理后循环自身,更新状态。
业务服务器

真正执行业务的服务器,主要功能集中在handle中,一个是添加,一个是查找。
编译服务器
启动服务器 并验证服务器功能是否正常运行
服务器处理
至此,一个最简单的存储用户上一次地址的服务器便制造出来了,至此我们先总结一下背后到底是如何运作的。

1.general_server:start(place_server,place_server).
启动服务器本体,第一个参数是注册的服务器进程的名字,第二个参数是我们真正处理业务的服务模组
2.place_server:add(shine957,home).
给用户添加一条地址信息,其内部其实是一个RPC(本质是 place_server其实充当了general_server的回调模块,同时还给客户端提供了接口),通过add接口,我们成功将信息发给了刚刚创建的以place_server为名注册的处理用户地址信息的进程
在这里插入图片描述
loop函数receive到了我们发起的add请求,同时去place_server:handle进行模式匹配
在这里插入图片描述
通过add我们匹配到了添加操作,将用户信息添加到字典,同时将新的字典返回,返回给用户一个操作成功的信息,同时通过尾递归自己更换NewState成功更新服务器的字典状态。

通用服务器之路——处理异常以及热更新

“凡是程序,皆有异常。”
我们写出的程序无法避免的会产生异常,正如人的一生会不停的犯错——改正——继续犯错如此循环,而出自我们程序员之手的服务程序自然也不可避免会产生各种各样的错误,我们称之为异常(exception)。
有异常,我们自然需要去处理异常,在其他的编程语言中,经常强调防御性编程——尽可能的阻止异常的产生,但是在erlang中不会,我们让异常尽情的产生——并尽可能在它产生的同时获得这个异常产生的原因并使它奔溃,然后我们在花时间修复这个异常。
接下来我们将制作一个能检测异常,并使发生了异常的客户端崩溃的服务器,还记得我们上面制作的gen_server吗,只需做一些小小的修改即可满足我们的要求
处理异常服务器
至此这个gen_server便完成了升级
rpc:我们在rpc远程调用里添加了一行新的指令,收到处理产生的crash指令时会使客户端exit程序,防止造成更大的损失。
loop:添加了try catch 以捕获服务器产生的异常(erlang的try catch是学习顺序型编程语言后引入的,_:Reason能捕获产生的一切异常信号,包括throw,exit等等)。
log_the_error:发生了错误,我们需要知道发生这个错误的原因,我们将捕获的异常打印出来。

同时得益于我们gen_server的功能来自于其回调模块,而不是其自身的特性,我们能够通过Mod的替换以达到服务器功能升级的效果,称之为热代码交换hot code swapping
实现也非常简单:
热代码交换
swap_code:传入新的回调模块的模块名称,发送给服务器。
同时我们在loop的服务器主体中添加一个swap_code命令分支,其效果就是替换服务器的回调模块的模块名称,并传入之前服务器所处的状态。
自此我们成功实现了检测异常处理异常以及完成服务器的热更新
接下来我们在原来的地址服务器上进行升级,给他添加一些没有的功能,来演示我们的代码热更新机制。
编译
我们给我们的新地址服务器添加了删除和查找所有功能,并且成功编译通过。启动服务器
可以看到我们现在成功启动了一个地址服务器,这个老的地址服务器并没有为其配置查找所有地址的功能,接下来我们传入新的回调模块使其升级!
传入新的代码
我们在服务器本体调用swap_code完成模块替换后,得到了功能上的增强,我们现在可以删除用户,以及获得我们这个服务器目前正在服务的所有用户。
验证exception
同时,我们手动写入一个产生exception的方法,并调用它,可以看到我们之前的catch模块正常捕获了这个异常,并打印了引起exception的原因:我们手动发起的exception请求没有可以匹配的函数子式导致。

通用服务器之路——完成一个job_center服务器(章节练习)

自前两个章节之后我们已经掌握了gen_server的基本原理,现在我们来入门gen_server,很简单,根据教程我们只需要三步即可实现一个最简单的gen_server:

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

第一步确定回调模块名,很简单,就叫他job_center即可,功能就和他的名字一样,为了处理job而生。
第二步编写接口函数,我们一个需要五个函数
接口函数
分别是启动函数star_link,其中?MODULE是一个宏定义,代表的是本模块的名字。
添加任务的函数add_job,给我们的任务队列添加job;job_wanted分配任务给我们的工人;job_done工人完成了任务需要上报给任务中心,任务中心需要确认任务已经不在队列;剩下的statistice用于统计还有哪些任务没有分配出去。
第三步在回调模块里编写六个必需的回调函数
六个回调函数
1.init 初始化服务器状态
2.handle_call处理工人对job_center的请求
3.handle_cast几乎同call,不过他是异步消息处理,并且他没有回复
4.handle_info用于处理和接口不相容的消息
5.terminate 终止服务器运行
6.code_change 代码热更新
当然仅仅是他们还不够,我们需要一些辅助函数来帮助我们处理一下业务
辅助函数
1.assign_job 用于分配任务给我们的工人,当没有任务的时候自然不会分配
2.send_job 我们需要告知工人成功接取到了这个任务
3.add与remove 处理任务队列的添加和删除
启动与初始化
成功启动了一个job_center服务器,start成功后会打印一个服务器的Pid
添加任务
给我们的任务中心添加了三条任务,并通过statistics查看
为工人分配任务
工人需要工作时为其分配任务,同时工人完成任务后需要告知服务器,服务器执行对应的清理工作
所有任务完成
当所有任务都已经完成之后,不再分配任务,返回工人的名称。
自此一个简单的建立于gen_server之上的job_center完成啦

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值