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:// ...
随机推荐
- UVA11996 Jewel Magic
思路 splay维护序列的hash值即可 因为有rev操作,还要维护反串的hash值 代码 #include <cstdio> #include <cstring> #incl ...
- HackerRank-Python攻城歷程-1.Tuples
Solution: if __name__ == '__main__': n = int(input()) integer_list = map(int, input().split()) t=tup ...
- Linux机器学习软件配置
如果需要安装Python+anaconda+pycharm,就不需要单独安装Python了,因为anaconda自带Python https://www.twblogs.net/a/5b7f8c742 ...
- hp quicktestprofession ver-10.0(QTP)的入门使用指南
---恢复内容开始--- SQA(software quality assurance) tool hp quicktestprofession ver-10.0(QTP) environment w ...
- hyperledger fabric 架设命令
单节点架设 Order 网络: cd ~/go/src/github.com/hyperledger/fabric/examples/e2e_cli/ rm -rf channel-artifacts ...
- Python3学习笔记----生成器
生成器的作用 生成器可以在循环的过程中推算出后续的元素,不必创建完整的list,从而节省大量空间. 生成器的创建 1.把一个列表生成式的 [ ] 改写为( ) # -*- coding: UTF-8 ...
- Python简单实现决策树
__author__ = '糖衣豆豆' #决策树 import pandas as pda fname="~/coding/python/data/lesson.csv" data ...
- [Linux]安装pyenv
python多版本管理pyenv 网址:https://blog.csdn.net/yingfeng_yang/article/details/82527321 Ubuntu16.04安装pyenv1 ...
- BaseDao封装
1.lombok 1) 在pom.xml中导入依赖 <!--导入lombok--> <!-- https://mvnrepository.com/artifact/org.proje ...
- 小程序tab切换 点击左右滑动
wxml <scroll-view scroll-x="true" class="navbar-box"> <block wx:for=&qu ...