非常教程

Erlang 20参考手册

指南:设计原则 | Guide: Design principles

总览 | 1. Overview

所述 OTP 设计原则定义如何构建的 Erlang 代码中的过程,模块,和目录条款。

1.1 监督树(Supervision Trees)

Erlang / OTP 中的基本概念是监督树。这是一个基于工人主管理念的流程构建模型:

  • 工人是执行计算的过程,也就是说,他们做实际的工作。
  • 主管是监督工作者行为的过程。如果出现问题,主管可以重新启动员工。
  • 监督树是代码到监督员和工作人员的分级安排,这使得设计和编程容错软件成为可能。

在下图中,方框代表主管,圆圈代表工作人员:

总览  |  1. Overview

图1.1:监督树

1.2 行为(Behaviours)

在监督树中,许多流程具有相似的结构,它们遵循类似的模式。例如,主管的结构相似。他们之间唯一的区别是他们监督哪个子进程。许多工作者都是服务器 - 客户端关系中的服务器,有限状态机器或错误记录器等事件处理程序。

行为是这些常见模式的形式化。这个想法是在通用部分(行为模块)和特定部分(回调模块)中划分进程的代码。

行为模块是 Erlang / OTP 的一部分。为了实现诸如监督员之类的过程,用户只需实现回调模块,该回调模块将导出预先定义的一组功能,即回调功能

以下示例说明如何将代码划分为通用部分和特定部分。考虑下面的代码(用简单的 Erlang 编写)为一个简单的服务器,它跟踪了一些“通道”。其它进程可通过分别调用函数alloc/0free/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

注意以下几点:

  • 代码server可以被重用来构建许多不同的服务器。
  • 服务器名称(在本例中为原子)ch2对客户端功能的用户是隐藏的。这意味着名称可以更改而不会影响它们。
  • 协议(发送到服务器和从服务器接收的消息)也被隐藏。这是一个很好的编程习惯,并且允许更改协议,而无需使用接口函数更改代码。
  • 可以扩展server功能,而无需更改ch2或任何其他回调模块。

ch1.erlch2.erl以上,实施channels/0alloc/1free/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模块与 Erlang / OTP 行为相对应,大大简化gen_server

标准的 Erlang / OTP 行为是:

  • gen_server 用于实现客户端 - 服务器关系的服务器
  • gen_statem

用于实现状态机

  • 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 带有许多组件,每个组件都实现了一些特定的功能。在Erlang / OTP 术语中这些组件被称为应用程序(Application) 。Erlang / OTP 应用程序的例子有 Mnesia,它具有编程数据库服务所需的所有功能,以及 Debugger,用于调试 Erlang 程序。基于 Erlang / OTP 的最小系统由以下两个应用程序组成:

  • 内核 - 运行 Erlang 所必需的功能
  • STDLIB - Erlang 标准库

应用程序概念同时适用于程序结构(进程)和目录结构(模块)。

最简单的应用程序没有任何进程,但由一组功能模块组成。这样的应用程序被称为库应用。库应用程序的一个示例是 STDLIB。

具有流程的应用程序最容易实现为使用标准行为的监督树。

如何编程应用程序在中描述Applications

1.4 发布(Releases)

发行版本是由 Erlang/ OTP 应用程序的子集和一组用户特定的应用程序所开发的完整的系统。

如何编程发布如下所述Releases

有关如何在目标环境中安装版本的信息,请参见第2章系统原则中有关目标系统的部分。

1.5 版本处理(Release Handling)

发布处理是在(可能)正在运行的系统中升级和降级发行版的不同版本。关于如何做到这一点请参见Release Handling

Erlang 20

Erlang 是一种通用的面向并发的编程语言,可应付大规模开发活动的程序设计语言和运行环境。

主页 https://www.erlang.org/
源码 https://github.com/erlang/otp
版本 20
发布版本 20.1