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:// ...
随机推荐
- CentOS7攻克日记(四) —— 安装Mysql和Redis
这一篇主要安装mysql,redis等数据库 在这篇开始之前,有一个坑,上一篇更改python软连接的时候,尽量都用名字是python3来软连接/usr/../bin/python3.6,把名字是 ...
- 学习笔记71—Python 报错处理集
****************************************************** 如有谬误,请联系指正.转载请注明出处. 联系方式: e-mail: heyi9069@gm ...
- 算法笔记--次小生成树 && 次短路 && k 短路
1.次小生成树 非严格次小生成树:边权和小于等于最小生成树的边权和 严格次小生成树: 边权和小于最小生成树的边权和 算法:先建好最小生成树,然后对于每条不在最小生成树上的边(u,v,w)如果我们 ...
- 如何运行一个Vue项目
一开始很多刚入手vue.js的人,会扒GitHub上的开源项目,但是发现不知如何运行GitHub上的开源项目,很尴尬.通过查阅网上教程,成功搭建好项目环境,同时对前段工程化有了朦朦胧胧的认知,因此将环 ...
- 雷林鹏分享:jQuery EasyUI 数据网格 - 自定义排序
jQuery EasyUI 数据网格 - 自定义排序 如果默认的排序行为不满足您的需求,您可以自定义数据网格(datagrid)的排序行为. 最基础的,用户可以在列上定义一个排序函数,函数名是 sor ...
- vue日历控件,自定义选择年月 选择年月日 选择年月日时 选择年月日时分,自定义日期范围
下载地址:https://pan.baidu.com/s/1iEZl4kDkEg4ybwqc7aI7vQ 注:功能更加全面的日历控件请访问:https://www.cnblogs.com/mrzhu/ ...
- c++连续读取未知个数的数字
#include <iostream> using namespace std; int main() { int n; ]; ,count=; while(cin>>n){ ...
- newcoder-最长树链-树/gcd
https://ac.nowcoder.com/acm/problem/13233 链接:https://ac.nowcoder.com/acm/problem/13233来源:牛客网 题目描述 树链 ...
- JS 超类和子类
此篇由别的大神的博客与<javascript高级程序设计>一书整理而来 原博客地址:https://hyj1254.iteye.com/blog/628555 看到javascript高级 ...
- mysql权限参考
mysql日常管理和应用工作中,大家经常会涉及到授权问题,下面,我们就列举下权限相关的参考. 1.管理权限(Administrative Privileges) Privilege Name ...