OTP&ETS

最近觉得实在是该梳理梳理erlang的框架内容了,所以整理了下。

OTP(开放电信平台):并发系统平台,
特点:容错(erlang:get_stacktrace(),try-catch,trap_exit进程退出时候发送信息{‘Exit‘,Pid,Reason},不会所有进程都崩溃而且可以由supervisor重启)--链路来容错、
监督(supervisor,重启机制)、
可升级(code_change(gen_server))、
并发(spawn,消息(而非java|c的锁))--进程&消息传递进行并发编程、
分布(nodes(),可以分布到多台机器上 erl-sname ...,rpc).
OTP目的:在于构建稳定、容错的系统。
appication:erlang对相关模块进行打包的一种手段。将这些模块做为一个整体。应用有自己的
生命周期:启动,完成任务,最后关闭。部分应用可以同时运行多个实例,另一些仅限一个。
supervisor:OTP最为重要的特性。监控其他进程,并在出现问题的时候重启故障,并向上汇报出现的问题。
还可以将监督者组织成监督树(更可靠)
-------------------------------------------------------------------------------------------
1.OTP的组织形式:
『1』遵循标准目录结构;
『2』添加用于存放应用元数据的.app文件
『3』创建一个application行为模式下实现模块,负责启动应用。
另外,应用行为模式中的start/2启动根监督者,实现supervisor行为模式的进程--统一派生和管理。
applicationName.app {application,Appliaction,[{description},{modules,[]},{mod,Start}...]}(application API 中有)
application_app.erl(behavior(application))
application_sup.erl(behavior(supervisor))
application_element.erl(具体处理问题的模块)
application_store.erl(用于存储oracle,or cache(redis))
-------------------------------------------------------------------------------------------
2.应用行为模式:
-module(application_app).
-behavior(application).
-export([start/2,
stop/1]).
start(_Type,_StartArgs)->
application_store:init(),
case application_sup:start_link() of
{ok,Pid}->
{ok,Pid};
Other ->
{error,Other}
end.
stop(_State)->
ok.
application两件事:初始化store及sup
-------------------------------------------------------------------------------------------
3.实现监控
-module(application_sup).
-behavior(supervisor).
-export([
start_link/0,
start_child/1 %%动态启动子进程
]).
-export([init/1]).

start_link()->
supervisor:start_link({local,?MODULE},?MODULE,[]).
%%{ok,State}.supervisor的是start_link的三个参数,第一个是:让OTP库在本地节点上以
MODULE为注册名自动注册监控进程。第三个参数给init的参数。

start_child(ChildSpec)->
supervisor:start_child(?MODULE,ChildSpec).

start_child(Value,LeaseTime)->
supervisor:start_child(?MODULE,[Value,LeaseTime]).
%%start_child(SupRef, ChildSpec) -> Result ChildSpec = child_spec() | [term()] Result = {ok,Child} | {ok,Child,Info} | {error,Error}
%% 生成子进程不一定都要childspec的六个参数,如果没有启动方式是simple_one_for_one就可以不定义这么多参数,而是像这个子进程,可以在后面将子进程需要的参数放入。
ChildSpec should be a valid child specification (unless the supervisor is a simple_one_for_one supervisor, see below). The child process will be started by using the start function as defined in the child specification.
If the case of a simple_one_for_one supervisor, the child specification defined in Module:init/1 will be used and ChildSpec should instead be an arbitrary list of terms List. The child process will then be started by appending List to the existing start function arguments, i.e. by calling apply(M, F, A++List) where {M,F,A} is the start function defined in the child specification.
init()->
Server ={application_server,{application_server,start_link,[]},temporary,brutal_kill,worker,[application_server]}, %%6个参数
Children = [Server],
RestartStrategy = {simple_one_for_one,0,1},
{ok,{RestartStrategy,Children}}.

RestartStrategy:{How,MaxCounts,TimeWithin}
ChildrenSpec:{ID,start{M,F,A},restart,shutdown,type,modules}
Restart:permanent总是重启|temporary永不重启|transient只有进程意外终止时重启
shutdown:指明如何终止进程,2000表示软关闭策略,留的自我了断时间,如果未在指定时间自行退出,则将被无条件终止。
brutal_kill:关闭监控进程时立即终止进程。
infinity:主要用于子进程本身也是监控进程,给予进程充分时间自行退出。
type:supervisor|worker
modules:该进程所以依赖的模块。
-------------------------------------------------------------------------------------------
-module(application_server).
-behavior(gen_server).
-export([init/1,
handle_call/3,
handle_cast/2,
handle_info/2,
terminate/2,
code_change/3
]).
-export([
start_link/2,
create/1,
create/2,
fetch/1,
replace/2,
delete/1]).

-record(state,{value,lease_time,start_time}).
-define(DEFAULT_LEASE_TIME,60*60*24).
%%五个基本方法用于实现,没有调用gen_server,而是gen_server的对应方法调用它们,所以参数是固定的。返回参数个数不是固定的。
init([Value,LeaseTime])->
Now = calendar:local_time(),
StartTime = calendar:datetime_to_gregorian_seconds(Now),
{ok,#state{value = Value,lease_time = LeaseTime,start_time = time_left(StartTime,LeaseTime)}}.
time_left(_StartTime,infinity)->
infinity;
time_left(_StartTime,LeaseTime)->
Now = calendar:local_time(),
CurrentTime = calendar:datetime_to_gregorian_seconds(Now),
TimeElapse = CurrentTime - StartTime,
case LeaseTime - TimeElapse of
Time when Time =< 0 -> 0;
Time -> Time * 1000
end.
handle_call(fetch,_From,State)->
#state{value = Value,lease_time = LeaseTime,start_time = StartTime} = State,
TimeLeft =time_left(StartTime,LeaseTime),
{reply,{ok,Value},State,TimeLeft}.
handle_cast({replace,Value},State)->
#state{value=Value,lease_time=LeaseTime,start_time=StartTime}=State,
{noreply,State}. %%noreply服务器不用发送任何应答继续运行就好
handle_call(delete,State)->
{stop,normal,State}.
%%stop,会导致gen_server自行终止,终止原因为normal,通知OTP系统服务器为正常关闭。除非进程为permanet负责不进行重启。application_sup中init定义的为temporary所以不会重启。
handle_info(timeout,State)->
{stop,normal,State}.
terminate(_Reason,_State)->
application_store:delete(self()), %%ets表中删除pid为自己的进程
ok.
%%gen_server关闭时,会调用terminate回调来执行一些清理操作,需要抹除与进程相关的键。键和pid的映射关系维护在appliction_store中,所以也要手动删除。
code_change(_OldVsn,State,_Extra)->
{ok,State}.
%%以下方法调用gen_server, 方法的参数不固定,只要给gen_server的对应方法对应参数即可,写的时候看下实现的方法handle的参数有无以及怎么写的
start_link(Value,LeaseTime)->
gen_server:start_link(?MODULE,[Value,LeaseTime],[]).
create(Value,LeaseTime)->
application_sup:start_child(Value,LeaseTime). %%supervisor的start_child(SupRef, ChildSpec) -> Result

create(Value)->
create(Value,?DEFAULT_LEASE_TIME).
fetch(Pid)->
gen_server:call(Pid,fetch).
replace(Pid,Value)->
gen_server:cast(Pid,{replace,Value}).
delete(Pid)->
gen_server:cast(Pid,delete).
---------------------------
-module().
-behavior(gen_server).
-export([init/1,
handle_call/3,
handle_cast/2,
handle_info/2,
terminate/2,
code_change/3
]).
-record(state,{}).
init([])->
{ok,#state{}}. %%完成初始化
handle_call(_Request,_From,State)->
Reply = ok,
{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}.
------------------------
------------------------
gen_server module Callback module
----------------- ---------------
gen_server:start_link -----> Module:init/1

gen_server:call
gen_server:multi_call -----> Module:handle_call/3

gen_server:cast
gen_server:abcast -----> Module:handle_cast/2

- -----> Module:handle_info/2

- -----> Module:terminate/2

- -----> Module:code_change/3
gen_server的
1. start_link(Module, Args, Options) -> Result
start_link(ServerName, Module, Args, Options) -> Result
2. call(ServerRef, Request) -> Reply
call(ServerRef, Request, Timeout) -> Reply %%ServerRef = Name | {Name,Node} | {global,GlobalName} | pid()
3. cast(ServerRef, Request) -> ok
-------------------------------------------------------------------------------------------
3.缓存系统的基本功能:
缓存的启动和停止;向缓存中添加(key-value),查询,更新,删除
API::
-module(application_store).
-export([
init/0,
insert/2,%% 同update,因为ets表中id唯一,如果后一个与前面的key相同,值自动覆盖
delete/1,
select/1
]).
-define(TABLE_ID,?MODULE).
init()->
ets:new(?TABLE_ID,[public,named_table]).
insert(Key,Pid)->
ets:insert(?TABLE_ID,{Key,Pid}).
select(Key)->
case ets:lookup(?TABLE_ID,Key) of
[{Key,Pid}]-> {ok,Pid};
[] -> {error,not_found}
end.
delete(Pid)->
ets:match_delete(?TABLE_ID,{‘_‘,Pid}). %%‘_‘匹配所有的值,{}内必须是全匹配;ets表中可以存入,key,value,value多个,不一定是键值对,而是键值值值...
---------------------------------
ets表的基本操作 (ets中key值唯一)
3> ets:new(aa,[public,named_table]). aa
4> ets:insert(aa,{key,value}). true
5> ets:lookup(aa,key). [{key,value}]
6> ets:insert(aa,{key,value1}). true
7> ets:lookup(aa,key). [{key,value1}]
32> ets:insert(aa,{value2,key,dd}). true
33> ets:lookup(aa,key). [{key,value1,dd}]
34> ets:match_delete(aa,{‘_‘,key,‘_‘}). true
35> ets:lookup(aa,value2). []
6> ets:insert(aa,{value2,key,aa}). true
37> ets:lookup(aa,value2). [{value2,key,aa}]
38> ets:match_delete(aa,{‘_‘,key}). true
39> ets:lookup(aa,value2). [{value2,key,aa}]

delete(Tab) -> true 表删除
delete(Tab, Key) -> true 删除内容(根据key删除)
match_delete(Tab, Pattern) -> true 模式匹配删除(根据value删除)
------------------------------------------------------------------------------------
4.
-_module(first_cache).
-export([
insert/2,
lookup/1,
delete/1]).
insert(Key,Value)->
case application_store:select(Key) of %%查看ets表中是否有,如果有就是key已经放入,将value更新到#state{}中
{ok,Pid}->
application_server:replace(Pid,Value);
{error,_}->
application_server:create(Value), %%创建子进程,并将value参数传入
application_store:insert(Key,Pid) %%存入ets表中
end.
lookup(Key)->
try %%先ets中看有没有
{ok,Pid} = application_store:select(Key), %%ets中都是key-pid对
{ok,Value} = application_server:fetch(Pid), %%handle_call会将#state{value}值返回
{ok,Value}
catch
_Class:_Exception -> %%三种:throw:Other ; exit:Reason; error:Reason
{error,not_found}
end.

delete(Key)-> %%只有lookup没有值应该error,其他多
case application_store:select(Key) of
{ok,pid}->
application_server:delete(Pid);
{error,_Reason}->
ok
catch

end.

时间: 2024-09-29 18:31:38

OTP&ETS的相关文章

[Erlang 0127] Term sharing in Erlang/OTP 上篇

之前,在 [Erlang 0126] 我们读过的Erlang论文 提到过下面这篇论文: On Preserving Term Sharing in the Erlang Virtual Machine 地址: http://user.it.uu.se/~kostis/Papers/erlang12_sharing.pdf  摘要:In this paper we describe our experiences and argue through examples why ?attening t

Erlang中的OTP简要

OTP包含了一组库和实现方式,可以构建大规模.容错和分布式的应用程序,包含了许多强大的工具,能够实现H248,SNMP等多种协议,核心概念是OTP行为,可以看作一个用回调函数作为参数的应用程序框架,类似一个J2EE容器.行为负责解决问题的非函数部分,回调函数负责解决函数部分. 通过gen_server模块可以实现事物语义和热代码交换, 1)      确定回调模块名 2)      编写接口函数 3)      在回调模块里编写6个必需的回调函数 当服务器崩溃时,需要一种机制来检测并重启它,要用

Erlang/OTP 中文手册

http://erldoc.com/ Open Telecom Platform application array asn1rt base64 binary calendar code dbg dict erlang ets file filelib gb_trees gen_tcp inet io lists make maps math mnesia net_adm os proplists random re rpc string sys unicode Erlang并发编程 Erlan

Erlang ets -- something about cache continue

上一次说到了实现一个简单cache 的基本思路和想法, http://www.cnblogs.com/--00/p/erlang_ets_something_about_cache.html 在文末, 说到了判断single record 内存占用量. 这次继续说说Erlang 数据项内存的相关问题. 在Erlang efficiency_guide 文档中, 较为清楚的表述了Erlang 系统中不同数据类型的内存消耗, 在这简单贴一两个: Small integer 1 wordOn 32-b

NetScaler OTP双因子身份认证登录演示

NetScaler OTP 应用场景 NetScaler OTP(one time password)是双因子身份证的一种,利用用户名密码+6位时间型令牌认证码,完成身份认证. 在以前的双因子解决方案中NetScalerGateway需要与第三方 Radius服务器集成,实现双因子认证.对于客户来说,需要额外支付双因子身份认证的费用,提高了解决方案成本. NetScaler OTP解决方案利用NetScaler 源生功能,配合手机APP  google authenticator,不需要其他成本

Erlang 104 OTP - incomplete

笔记系列 Erlang环境和顺序编程Erlang并发编程Erlang分布式编程YawsErlang/OTP 日期              变更说明 2014-12-21 A Outline, 1 Agenda 0 Scope 围绕OTP设计原则,分别记录行为模式.监督树概念.应用.发布和部署,以及[3]中一个产品级缓存解决方案. 1 OTP Design Principles Erlang Doc: OTP Design Principles User's Guide OTP设计原则阐述的是如

OTP supervisor的monitor_child是否有漏洞

问题描述 OTP的supervisor中为了防止淘气的Child从link的另一端断掉link,supervisor会在shutdown child之前unlink(Child)并切换为monitor状态,这样supervisor对Child的监控将无法被Chlid终止.这段代码是由monitor_child/1实现的,其具体实现代码如下: 872 %% Help function to shutdown/2 switches from link to monitor approach 873

OTP的supervisor tree如何保证子进程一定随父进程的退出而退出

利用OTP行为包构建的应用之所以可靠,是因为我们按照OTP的设计模式,将所有进程组织成了一棵可靠的supervisor tree.每一个supervisor监控其子进程,并在其子进程出错时按照重启策略进行相应的处理. 但是,你是否考虑过,如果supervisor意外终止,其子进程会怎样?当然,直觉告诉我们连监控进程的没有了,所有的子进程应全部终止.但是,你在代码中是否真正考虑过这种情况?你的gen_server可否写过如下代码? handle_info({'EXIT', Parent, Reas

Erlang ETS Table

不需要显示用锁,插入和查询时间不仅快而且控制为常量,这就是Erlang的ETS Table. 为什么而设计? Erlang中可以用List表达集合数据,但是如果数据量特别大的话在List中访问元素就会变慢了;这种主要是由于List的绝大部分操作都是基于遍历完成的. Erlang的设计目标是软实时(参考:http://en.wikipedia.org/wiki/Real-time_computing),在大量数据中检索的时间不仅要快而且要求是常量.为了解决快速查 询的问题,Erlang提供的机制就