`
coderplay
  • 浏览: 576832 次
  • 性别: Icon_minigender_1
  • 来自: 广州杭州
社区版块
存档分类
最新评论

OTP设计原则:概要

阅读更多

1 Overview

OTP 设计原则 是一套教你如何运用进程,模块和目录等条件来组织Erlang代码的原则.

1.1 Supervision Trees 管理树

A basic concept in Erlang/OTP is the supervision tree. This is a process structuring model based on the idea of workers and supervisors.
Erlang/OTP中的一个基本概念就是 管理树. 这是一个以工作者管理者思想为基础的进程结构模型.

  • Workers are processes which perform computations, that is, they do the actual work.
    工作者是完成计算的进程,也就是说,它们干实际的活。
  • Supervisors are processes which monitor the behaviour of workers. A supervisor can restart a worker if something goes wrong.
    管理者是监视工作者行为的进程.一个管理者可以 在出错的时候重启一个工作者.
  • The supervision tree is a hierarchical arrangement of code into supervisors and workers, making it possible to design and program fault-tolerant software.
    管理树是一些管理者和工作者的分层排布代码.它使得设计和编写容错软件成为可能.

sup6
Supervision Tree 管理树

In the figure above, square boxes represents supervisors and circles represent workers.
上图中, 方块代表管理者, 圆代表工作者.

1.2 Behaviours 行为

In a supervision tree, many of the processes have similar structures, they follow similar patterns. For example, the supervisors are very similar in structure. The only difference between them is which child processes they supervise. Also, many of the workers are serves in a server-client relation, finite state machines, or event handlers such as error loggers.
在一棵管理树中, 许多进程都有相似的结构,它们遵循相似的模式.例如, 所有管理者的结构非常相似.它们之间唯一不同点就是具体管理哪些子进程.同样, 许多工作者都在服务-客户关系, 有限状态机或事件处理中提供服务.

Behaviours are formalizations of these common patterns. The idea is to divide the code for a process in a generic part (a behaviour module) and a specific part (a callback module).
行为形式化了这些共同模式.它的思想是把进程的代码分离成一普通部分(一个行为模块)和一特殊部分(一个回调模块).

The behaviour module is part of Erlang/OTP. To implement a process such as a supervisor, the user only has to implement the callback module which should export a pre-defined set of functions, the callback functions.
行为模块是Erlang/OTP的一部分。 实现像管理者这样的进程, 用户只须实现回调模块.回调模块要求输出一套预定义函数,这些函数叫做回调函数。

An example to illustrate how code can be divided into a generic and a specific part: Consider the following code (written in plain Erlang) for a simple server, which keeps track of a number of "channels". Other processes can allocate and free the channels by calling the functions alloc/0 and free/1, respectively.

代码
 
  1. -module(ch1).  
  2. -export([start/0]).  
  3. -export([alloc/0, free/1]).  
  4. -export([init/0]).  
  5.   
  6. start() ->  
  7.     spawn(ch1, init, []).  
  8.   
  9. alloc() ->  
  10.     ch1 ! {self(), alloc},  
  11.     receive  
  12.         {ch1, Res} ->  
  13.             Res  
  14.     end.  
  15.   
  16. free(Ch) ->  
  17.     ch1 ! {free, Ch},  
  18.     ok.  
  19.   
  20. init() ->  
  21.     register(ch1, self()),  
  22.     Chs = channels(),  
  23.     loop(Chs).  
  24.   
  25. loop(Chs) ->  
  26.     receive  
  27.         {From, alloc} ->  
  28.             {Ch, Chs2} = alloc(Chs),  
  29.             From ! {ch1, Ch},  
  30.             loop(Chs2);  
  31.         {free, Ch} ->  
  32.             Chs2 = free(Ch, Chs),  
  33.             loop(Chs2)  
  34.     end.  
  35.       

The code for the server can be rewritten into a generic part server.erl:
服务的代码可以写入到一普通部分server.erl:

代码
 
  1. -module(server).  
  2. -export([start/1]).  
  3. -export([call/2, cast/2]).  
  4. -export([init/1]).  
  5.   
  6. start(Mod) ->  
  7.     spawn(server, init, [Mod]).  
  8.   
  9. call(Name, Req) ->  
  10.     Name ! {call, self(), Req},  
  11.     receive  
  12.         {Name, Res} ->  
  13.             Res  
  14.     end.  
  15.   
  16. cast(Name, Req) ->  
  17.     Name ! {cast, Req},  
  18.     ok.  
  19.   
  20. init(Mod) ->  
  21.     register(Mod, self()),  
  22.     State = Mod:init(),  
  23.     loop(Mod, State).  
  24.   
  25. loop(Mod, State) ->  
  26.     receive  
  27.         {call, From, Req} ->  
  28.             {Res, State2} = Mod:handle_call(Req, State),  
  29.             From ! {Mod, Res},  
  30.             loop(Mod, State2);  
  31.         {cast, Req} ->  
  32.             State2 = Mod:handle_cast(Req, State),  
  33.             loop(Mod, State2)  
  34.     end.  


and a callback module ch2.erl:
加上一个加调模块 ch2.erl:

代码
 
  1. -module(ch2).  
  2. -export([start/0]).  
  3. -export([alloc/0, free/1]).  
  4. -export([init/0, handle_call/2, handle_cast/2]).  
  5.   
  6. start() ->  
  7.     server:start(ch2).  
  8.   
  9. alloc() ->  
  10.     server:call(ch2, alloc).  
  11.   
  12. free(Ch) ->  
  13.     server:cast(ch2, {free, Ch}).  
  14.   
  15. init() ->  
  16.     channels().  
  17.   
  18. handle_call(alloc, Chs) ->  
  19.     alloc(Chs). % => {Ch,Chs2}  
  20.   
  21. handle_cast({free, Ch}, Chs) ->  
  22.     free(Ch, Chs). % => Chs2  

Note the following:
注解:

  • The code in server can be re-used to build many different servers.
    server代码可以重用到任何其它不同的服务中.
  • The name of the server, in this example the atom ch2, is hidden from the users of the client functions. This means the name can be changed without affecting them.
    服务名称,在这儿是ch2原子.它对其它客户函数的使用来说是不可见的.这意味着名称的修改将不会影响到其它客户函数.
  • The protcol (messages sent to and received from the server) is hidden as well. This is good programming practice and allows us to change the protocol without making changes to code using the interface functions.
    协议(传送到或接收自服务器的消息)被隐藏很得很好. 这是一个良好的编程,且使我们可以改变协议的同时,不必改变接口函数的代码.
  • We can extend the functionality of server, without having to change ch2 or any other callback module.
    我们可以扩展server的功能,而不必改变ch2或其它任何回调模块. 

(In ch1.erl and ch2.erl above, the implementation of channels/0, alloc/1 and free/2 has been intentionally left out, as it is not relevant to the example. For completeness, one way to write these functions are given below. Note that this is an example only, a realistic implementation must be able to handle situations like running out of channels to allocate etc.)

代码
 
  1. channels() ->  
  2.    {_Allocated = [], _Free = lists:seq(1,100)}.  
  3.   
  4. alloc({Allocated, [H|T] = _Free}) ->  
  5.    {H, {[H|Allocated], T}}.  
  6.   
  7. free(Ch, {Alloc, Free} = Channels) ->  
  8.    case lists:member(Ch, Alloc) of  
  9.       true ->  
  10.          {lists:delete(Ch, Alloc), [Ch|Free]};  
  11.       false ->  
  12.          Channels  
  13.    end.     

Code written without making use of behaviours may be more efficient, but the increased efficiency will be at the expense of generality. The ability to manage all applications in the system in a consistent manner is very important.

Using behaviours also makes it easier to read and understand code written by other programmers. Ad hoc programming structures, while possibly more efficient, are always more difficult to understand.

The module server corresponds, greatly simplified, to the Erlang/OTP behaviour gen_server.

The standard Erlang/OTP behaviours are:

gen_server
For implementing the server of a client-server relation.
实现客户-服务关系的服务端.
gen_fsm
For implementing finite state machines.
实现有限状态机.
gen_event
For implementing event handling functionality.
实现事件处理功能.
supervisor
For implementing a supervisor in a supervision tree.
在管理树中,实现管理者.

The compiler understands the module attribute -behaviour(Behaviour) and issues warnings about missing callback functions. Example:
编译器能识别模块属生-behaviour(Behaviour),并会在缺少回调函数时发出警告.例如:

代码
 
  1. -module(chs3).  
  2. -behaviour(gen_server).  
  3. ...  
  4.   
  5. 3> c(chs3).  
  6. ./chs3.erl:10: Warning: undefined call-back function handle_call/3  
  7. {ok,chs3}  
  8.       

1.3 Applications 应用

Erlang/OTP comes with a number of components, each implementing some specific functionality. Components are with Erlang/OTP terminology called applications. Examples of Erlang/OTP applications are Mnesia, which has everything needed for programming database services, and Debugger which is used to debug Erlang programs. The minimal system based on Erlang/OTP consists of the applications Kernel and STDLIB.
Erlang/OTP带有一系列组件,它们分别实现了特定的功能。组件在Erlang/OTP术语中叫做应用.Erlang/OTP应用有众多例子。命例如: Mnesia,它有所有数据库服务编程所需的功能; 调试器,它用来调试Erlang程序;最小系统,它基于Erlang/OTP,由Kernel 与STDLIB应用组成.

The application concept applies both to program structure (processes) and directory structure (modules).
应用概念既适用于程序结构(进程),也适用于目录结构(模块).

The simplest kind of application does not have any processes, but consists of a collection of functional modules. Such an application is called a library application. An example of a library application is STDLIB.
最简单的应用没有任何进程,而是由一套功能模块组成. 这种应用叫做库应用.库应用的一个典型例子就是STDLIB.

An application with processes is easiest implemented as a supervision tree using the standard behaviours.
最容易实现且带有进程的应用,就是用标准行为实现的管理树.

How to program applications is described in Applications.
怎么编写应用,在  Applications中有描述.

1.4 Releases 发行

A release is a complete system made out from a subset of the Erlang/OTP applications and a set of user-specific applications.
一个release 是一个由Erlang/OTP 子集与一些用户特定的应用组成的完整系统

How to program releases is described in Releases.
怎么编写releases请参考 Releases.

How to install a release in a target environment is described in the chapter about Target Systems in System Principles.
怎么在目标环境中安装一个release,Target Systems in System Principles相关的章节有描述.

1.5 Release Handling

Release handling is upgrading and downgrading between different versions of a release, in a (possibly) running system. How to do this is described in Release Handling.
Release handling 是在一个运行系统中,不同版本release之间的升级和降级 ,具体怎么做,Release Handling.有描述.

  • 描述: supervisor/worker图示6
  • 大小: 1.6 KB
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics