什么是OTP编程?
可以看作一个用回调函数作为参数的应用程序框架,利用gen_server模块可以实现事物语义和代码热更新,OTP是Open Telecom Platform的缩写,它能帮我们编写大型的、容错的,分布式的系统。刚看这本书的时候,joe大爷说,我们来领悟Erlang中的哲学吧,我寻思又是一些什么奇奇怪怪的东西,其实也就是想向读者表达Erlang的世界观:"一切皆为进程",进程是Erlang的基础设施,不管要完成什么功能,进程都会遵循通用的行为模式,这些进程模式可以称为模式的模式;我们直接使用进程做开发的时候会遵循这个模式,behavior的实现同样遵循进程模式.OTP behaviour包含gen_server gen_event gen_fsm supervisor.其中绝大多数情况下都是在使用gen_server,supervisor本身也是使用gen_server实现的.我们就以gen_server做为起点,逐步学习Erlang OTP.
1.gen_server模块(OTP中其中一种抽象的编程模式)
按照joe大爷的脑回路,想要深入学习一门东西并领悟精髓,就是去了解它的原理,去模仿他,类似重构,在这里我就不再把书上的代码贴出来了,我直接把我所理解的gen_server表达出来:
-
1.事物语义的实现
什么是事物语义呢?说实话我解释不出来,我只知道书上的代码,会截取抛出的错误,并用错误之前的上下文(书上称是服务器的状态,但我觉得上下文更符合)继续运行软件,也就是服务器时光倒退到了发生错误之前的一刻,而没发生错误的时候,时时刻刻都在更新服务器的上下文。 -
2.热代码更新的实现
gen_server模块自带了热更新的功能,Erlang允许程序代码在运行系统中被修改。旧代码能被逐步淘汰而后被新代码替换。在此过渡期间,新旧代码是共存的。 -
3.gen_server的组成
在我看来,最主要就是分为:回调函数(handle_call抽象函数的实例化)、接口函数(供用户调用)、启动\停止程序
handle_call(Recv,From,State)->{reply,Reply,newState}
%% Recv通常为tag tuple也就是带标签的元组,这样就可以重载多个回调函数,例如{action,A,B}.
%% State是旧的服务器状态,也就是我说的上下文环境
%% Reply是我们自定义的返回数据
%% newState是我们在回调函数中一系列的操作后,变更的上下文环境
handle_info(Info,State)
%% 通过这个函数可以接收一些非自发性的消息比如推出信号
terminate(Reason,State)->ok.
%% 在收到exit信号或者{stop,Reason,NewState}时会被调用
code_change(OldVsn,State,Extra)->{ok,NewState}
%% 在gen_server自动更新代码时被调用
gen_server:start_link({local|global,Name},Mod,[],[])->bool()
%% local和global的区别是前者在本机可调用,global可跨节点
%% Name是为进程注册的原子名称
%% Mod是init([])函数所在的模块名、一般回调和接口函数都在一个模块
init([])->{ok,State}
%%gen_server开始start_link的时候会调用init函数来生成一个初始化的服务器上下文环境
gen_server:call(Name,Request)->Reply
%% 这个函数我们在接口函数会用到,用来远程调用函数
%% Name是我们start_link的时候注册的名称
%% Reuqset其实就是handle_call的时候接收的Recv,带标签的元组,往往第一个原子元素就是我们的动作标识,比如写入分数:{setPoint,Who,100}
%% Reply就是我们在回调函数中的Reply自定义回复数据
gen_server:cast(Name,Request)->{noreply,State}|...
%% 这个远程远程调用函数不会接收返回值
一下贴上我自己对gen_server练习的代码:
-module(otp_server).
-export([start/0,stop/0,init/1,setPoint/2,deletePoint/1,getPoint/1,handle_call/3,handle_cast/2,handle_info/2,terminate/2,code_change/3]).
-behaviour(gen_server).
% Server Start
start()->
gen_server:start_link({local,?MODULE},?MODULE,[],[]).
init([])->
{ok,ets:new(?MODULE,[])}.%Return a new State
stop()->
gen_server:call(?MODULE,stop).
% function call
setPoint(Who,Point)->
gen_server:call(?MODULE,{set,Who,Point}).
deletePoint(Who)->
gen_server:call(?MODULE,{delete,Who}).
getPoint(Who)->
gen_server:call(?MODULE,{get,Who}).
% Handle call
handle_call({set,Who,Point},_From,TableId)->
ets:insert(TableId,{Who,Point}),
Reply={Who,set_point_success},
{reply,Reply,TableId};
handle_call({delete,Who},_From,TableId)->
case ets:lookup(TableId,Who) of
[]->
Reply={Who,this_guy_isnot_exist};
[_]->
Reply=ets:delete(TableId,Who)
end,
{reply,Reply,TableId};
handle_call({get,Who},_From,TableId)->
case ets:lookup(TableId,Who) of
[]->
Reply={Who,this_guy_isnot_exist};
[{Who,Point}]->
Reply={Who,point_is,Point}
end,
{reply,Reply,TableId};
handle_call(stop,_From,TableId)->
{stop,normal,TableId};
handle_call(_,_From,TableId)->
{reply,undefine_command,TableId}.
handle_cast(_Msg,State)->
{noreply,State}.
handle_info(_Info,State)->
{noreply,State}.
terminate(_Reason,_State)->
ok.
code_change(_OldVsn,State,_Extra)->
{ok,State}.
网友评论