您的位置:首页 > 编程语言 > PHP开发

OTP设计原则学习笔记(1)

2009-01-17 05:50 441 查看
1、概述

OTP设计原则,是对编写进程、模块、目录等结构化程序代码的一般要求。

1.1 监管树(Supervision Trees)

Erlang/OTP的一个基本概念是监督管理树。它是结构化进程的模型,它的理论基础是“执行人”和“监管人”的监管执行机制。

● 执行人是从事实际运算工作的进程。

● 监管人是监督管理执行人具体工作的进程。发生执行错误,监管人可以命令执行人重新开始工作。

● 监管树把监管人与执行人的关系,组织成分层有序的结构,具有容错机制。

1.2 行为(Behaviours)

监督树中的许多进程,结构相同,模式相同。例如,监督人的结构十分相似,唯一的区别是其监管着不同的子进程。同样,许多执行人的工作是向客户提供服务,或者,充当有限状态机,以及事件处理器譬如书写差错日志。

行为的概念,是对上述工作模式的进一步抽象。目的是为了把进程代码分成2部分,一般属性(行为模块)和具体行动(响应模块)。

行为模块是Erlang/OTP的组成部分。为了实现进程,如监管人,程序员必须实现行动模块,导出预定义的响应函数。

下面的例子,把进程代码分成了2部分:一个简单的服务器,跟踪着许多“通道”。其他进程可以调用函数alloc/0或free/1,分配或释放这些通道。

-module(ch1).

-export([start/0]).

-export([alloc/0, free/1]).

-export([init/0]).

start() ->

spawn(ch1, init, []).

alloc() ->

ch1 ! {self(), alloc},

receive

{ch1, Res} ->

Res

end.

free(Ch) ->

ch1 ! {free, Ch},

ok.

init() ->

register(ch1, self()),

Chs = channels(),

loop(Chs).

loop(Chs) ->

receive

{From, alloc} ->

{Ch, Chs2} = alloc(Chs),

From ! {ch1, Ch},

loop(Chs2);

{free, Ch} ->

Chs2 = free(Ch, Chs),

loop(Chs2)

end.

服务器的代码,作为一般属性部分,可以写入文件server.erl:

-module(server).

-export([start/1]).

-export([call/2, cast/2]).

-export([init/1]).

start(Mod) ->

spawn(server, init, [Mod]).

call(Name, Req) ->

Name ! {call, self(), Req},

receive

{Name, Res} ->

Res

end.

cast(Name, Req) ->

Name ! {cast, Req},

ok.

init(Mod) ->

register(Mod, self()),

State = Mod:init(),

loop(Mod, State).

loop(Mod, State) ->

receive

{call, From, Req} ->

{Res, State2} = Mod:handle_call(Req, State),

From ! {Mod, Res},

loop(Mod, State2);

{cast, Req} ->

State2 = Mod:handle_cast(Req, State),

loop(Mod, State2)

end.

第二部分是响应模块 ch2.erl:

-module(ch2).

-export([start/0]).

-export([alloc/0, free/1]).

-export([init/0, handle_call/2, handle_cast/2]).

start() ->

server:start(ch2).

alloc() ->

server:call(ch2, alloc).

free(Ch) ->

server:cast(ch2, {free, Ch}).

init() ->

channels().

handle_call(alloc, Chs) ->

alloc(Chs). % => {Ch,Chs2}

handle_cast({free, Ch}, Chs) ->

free(Ch, Chs). % => Chs2

注意:

● 上述服务器代码,可用于建设许多不同类型的服务器;

● 例中服务器的名字ch2,对于其服务对象的函数是看不到的,因此,可以改变名字;

● 服务器收发消息的协议,也是调用它的函数不知道的。这是好的编程习惯,它让我们用接口函数改变协议。

● 我们可以扩展server.erl中函数的功能,而不必修改ch2.erl和其他响应模块。

(在ch1.erl和ch2.erl中,有意忽视函数channels/0, alloc/1 和free/2,似乎它们可有可无。其实,完整的函数在后面。这里只是个示例,而实际的程序必须能够处理复杂情况,如分配完成任务的通道,等等。)

channels() ->

{_Allocated = [], _Free = lists:seq(1,100)}.

alloc({Allocated, [H|T] = _Free}) ->

{H, {[H|Allocated], T}}.

free(Ch, {Alloc, Free} = Channels) ->

case lists:member(Ch, Alloc) of

true ->

{lists:delete(Ch, Alloc), [Ch|Free]};

false ->

Channels

end.

不用行为模式的程序代码可能效率更高,但总体代价也大。十分重要的是,要在系统中统一管理全部应用程序。

使用行为模式,有利于程序代码易读易懂。执行效率高的代码,往往难以看懂。

模块server.erl,类似Erlang/OTP 的行为模式 gen_server,但做了极大简化。

Erlang/OTP的标准行为模式包括:

gen_server

实现client-server关系中的服务器;

gen_fsm

实现有限状态机;

gen_event

实现事件处理功能;

supervisor

在监管树中实现监管人。

如果定义了 -behaviour(Behaviour) 而没有定义响应函数,编译器会发出警告:

-module(chs3).

-behaviour(gen_server).

...

3> c(chs3).

./chs3.erl:10: Warning: undefined call-back function handle_call/3

{ok,chs3}

1.3 代码部件applications


Erlang/OTP有许多代码部件,分别实现特定功能。这些部件的术语叫applications,如Mnesia、Debugger。在Erlang/OTP 基础之上建立的最小系统,其applications是Kernel 和 STDLIB。

应用程序的概念有2层含义,程序结构(进程)和目录结构(模块)。

最简单的应用程序没有进程,只有一些函数组成的模块。它就是库程序,例如STDLIB。

应用程序最简单的实现,是用标准的行为模式,做成监管树。

如何具体编程,后面会讲。

1.4 发布的版本

发布的版本,是指由Erlang/OTP的系统子程序发展出来的、结合用户提供的应用程序组成的完整系统。

如何编制发行的版本,后面会讲。

如何在大型环境中安装发布的版本,后面会讲。

1.5 版本管理


版本管理,是指在发布版本之间的升级(更新)和降级(复旧)。具体如何做,后面会讲。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: