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

 

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 版本管理

版本管理,是指在发布版本之间的升级(更新)和降级(复旧)。具体如何做,后面会讲。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值