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.
OTP&ETS的更多相关文章
- 转载: Erlang Socket解析二进制数据包
转自:http://www.itkee.com/developer/detail-318.html 今天在家里闲来无事,实践了一下Erlang的Socket的功能.记录一下在过程中遇到的一些问题,以及 ...
- [Erlang 0127] Term sharing in Erlang/OTP 上篇
之前,在 [Erlang 0126] 我们读过的Erlang论文 提到过下面这篇论文: On Preserving Term Sharing in the Erlang Virtual Machine ...
- [Erlang 0119] Erlang OTP 源码阅读指引
上周Erlang讨论群里面提到lists的++实现,争论大多基于猜测,其实打开代码看一下就都明了.贴出代码截图后有同学问这代码是哪里找的? "代码去哪里找?",关于Erla ...
- Erlang ets -- something about cache continue
上一次说到了实现一个简单cache 的基本思路和想法, http://www.cnblogs.com/--00/p/erlang_ets_something_about_cache.html 在文末, ...
- Erlang/OTP 中文手册
http://erldoc.com/ Open Telecom Platform application array asn1rt base64 binary calendar code dbg di ...
- Erlang模块ets翻译
概要: 内置的存储 描述: 这个模块是Erlang内置存储BIFs的接口.这些提供了在Erlang运行时系统中存储大量数据的能力,并且能够对数据进行持续的访问时间.(在ordered_set的情况下, ...
- 动态令牌-(OTP,HOTP,TOTP)-基本原理
名词解释和基本介绍 OTP 是 One-Time Password的简写,表示一次性密码. HOTP 是HMAC-based One-Time Password的简写,表示基于HMAC算法加密的一次性 ...
- Erlang/OTP 17.0-rc1 新引入的"脏调度器"浅析
最近在做一些和 NIF 有关的事情,看到 OTP 团队发布的 17 rc1 引入了一个新的特性“脏调度器”,为的是解决 NIF 运行时间过长耗死调度器的问题.本文首先简单介绍脏调度器机制的用法,然后简 ...
- 利用OTP为odoo增强安全访问
两次验证是广泛应用于各大站点的验证机制,我们今天利用Google Authentication来实现Odoo的两次验证,防止撞库或密码泄露等引起的安全问题. 1. 二次验证的原理 参见 http:// ...
随机推荐
- 【SSL Certificates】什么是数字证书(Certificates)?
本文涉及的相关问题,如果你的问题或需求有与下面所述相似之处,请阅读本文 ssl certificate 什么是ssl certificates? SSL Certificates 是一种使用数字加密技 ...
- VMware15 安装centos7标准板
VM主页——>创建虚拟机——>典型——>下一步: 选择安装安装操作系统,进入选择客户机操作系统界面 选择Linux 版本centos7 64位: 下一步——>填写虚拟机名称, ...
- WEB前端基础知识点
因为要告知浏览器的解析器用什么文档标准解析这个文档,所以在文档的开头要写上文档类型声明,H5的文档类型声明要比H4文档类型声明简洁的多.因为H5不基于SGML(标准通用标记语言),所以不需要对DTD文 ...
- 根据不同浏览器判断OCX插件是否安装
最近项目进入到了验收阶段,需要兼容不同的浏览器,海康的Demo写了一个判断插件是否成功安装的函数,但是经过测试,只在IE浏览器下有效果,在其他的浏览器下面会出现Bug,现在需要写一个通用的方法,在不同 ...
- jmeter需要学习的其他点
jmeter知识点访问地址参数化http请求默认值远程连接非GUI运行测试命令jmeter参数设置jmeter常用脚本开发 Beanshell sampleDebug SampleFtp请求java请 ...
- CentOS7.X怎样更新yum源
1. 备份原来的yum源 $sudo cp /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo-backup 2.设 ...
- 给web请求加遮罩动画
效果预览: css: /*#fountainG{ position:relative; margin:10% auto; width:240px; height:29px }*/ #fountainG ...
- 关于ip通信学习感想
在没有接触过ip通信之前,我对于网络的认识非常浅薄,比如上网只需要交钱和一根网线就可以上网,但自从上了第一节课之后,感觉打开了新世界的大门.我国的移动通信公司也没有权利单独分配独有的ip地址,还要看亚 ...
- java的抽象方法为什么不能是static、final、private?
1.java的抽象方法为什么不能用static修饰?类抽象方法? 如上代码,在抽象类中定义static属性是没有问题的,但是定义抽象方法时是不能定义为静态(static)的,否则编译器会报错:The ...
- php 基于tcpdf插件生成pdf
之前在公司做了个项目,,需要导出pdf合同,,在网上找了很久,选择用了tcpdf插件,,具体的插件网上可以搜到,中间遇到了很多的坑,慢慢的填. 先下好插件放到指定文件夹下 然后使用tcpdf插件里ht ...