Erlang OTP Application

2020-06-23  本文已影响0人  坚果jimbowhy

Erlang OTP Application

Erlang 应用程序就是一组相关代码和进程,使用 OTP 框架的程序就是 Erlang/OTP 应用程序。

启动 Erlang shell 后,默认至少运行了以下两个程序:

不要和将这里运行的 STDLIB 程序和标准库混乱,这是两个不同的东西,一般来说,还有一个 sasl 程序也是经常需要用到的,因为这三个应用程序是 Erlang 系统中运行的最基本程序。

Kernel 应用程序拥有运行基础代码以运行 Erlang runtime system,包括文件服务、代码服务等等,它是第一运行和程序。它也是强制运行的,因为基于 Erlang/OTP 最小系统就包括 Kernel 和 STDLIB 两个程序。

STDLIB 应用程序不提供服务,Kernel 应用程序功能如下:

注意 Erlang/OTP 的 SASL 应用与 RFC 4422 文档中的 Simple Authentication and Security Layer 没有任何关系。SASL 全称 System Architecture Support Libraries,为 Erlang/OTP 应用程序架构提供了以下机制支持,主要用于应用程序的发布打包升级等:

Erlang/OTP 工程的基本框架,即 Supervision Tree 架构:

Supervisor 负责启动,停止和监视其子进程,基本思想是通过在必要时重新启动它们来保持子进程的活动。

在 Erlang/OTP 架构中,一切进程都是轻量级的,都可以被监控 monitor,有 Supervisor 专门做监控。你可以方便的用一个 Supervisor 进程去管理子进程,它会根据你设定的策略,来处理意外挂掉的子进程。这种情况的问题的是,错误处理稍微做不好就会挂,Restart Strategy 重启策略有:

在监督树中,许多流程具有相似的结构,它们遵循类似的模式,即抽象为 Behaviour 模型。Supervisor 的结构相似,他们之间唯一的区别是他们监督哪个子进程。许多 Worker 都是 C/S 服务器对客户端关系模式中的服务器角色,Worker 对应各种 Behaviour,包括有限状态机器 gen_statem、错误事件记录器 gen_event 等事件处理程序,还有 gen_server 通用服务器行为。

总结起来,Erlang/OTP 系统就是三大基础应用程序,四大 Behaviour 中,除 Supervisor 外,都在监督树充当 Worker 角色:

推荐的开发阶段使用的目录结构:

─ ${application}
  ├── doc
  │   ├── internal
  │   ├── examples
  │   └── src
  ├── include
  ├── priv 
  ├── src 
  │   └── ${application}.app.src
  └── test

其它目录可以根据需要添加,比如 c_src 存放 C 代码,java_src 存放 Java 代码,go_src 存放 Go 代码。

发布后,推荐使用的应用程序目录结构:

─ ${application}-${version}
  ├── bin
  ├── doc
  │   ├── html
  │   ├── man[1-9]
  │   ├── pdf
  │   ├── internal
  │   └── examples
  ├── ebin
  │   └── ${application}.app
  ├── include
  ├── priv
  │   ├── lib
  │   └── bin
  └── src

在经典 Erlang/OTP 程序框架中,Supervision Tree 框架应用程序实现了各种上回调模块,包括 Application Callback Module,它提供了两个函数来启动或终止应用:

参数解析:

在 Erlang 中,程序可以包含其它任意个程序,Included Applications,有独立的目录结构,子程序同时只能被一个程序拥有,通常通过 Supervision Tree 启动。而只包含子程序的顶级程序,叫做主程序 Primary Application。它们可以在 .app 文件中配置,子程序在启动时的同步可以使用 start_phases 设置,而 mod 必需设置为 {application_starter,[Module,StartArgs]},StartArgs 会在子程序启动时传入 Module:start/2 函数:

{application, prim_app,
 [{description, "Tree application"},
  {vsn, "1"},
  {modules, [prim_app_cb, prim_app_sup, prim_app_server]},
  {registered, [prim_app_server]},
  {included_applications, [incl_app]},
  {start_phases, [{init,[]}, {go,[]}]},
  {applications, [kernel, stdlib, sasl]},
  {mod, {application_starter,[prim_app_cb,[]]}},
  {env, [{file, "/usr/local/log"}]}
 ]}.

{application, incl_app,
 [{description, "Included application"},
  {vsn, "1"},
  {modules, [incl_app_cb, incl_app_sup, incl_app_server]},
  {registered, []},
  {start_phases, [{go,[]}]},
  {applications, [kernel, stdlib, sasl]},
  {mod, {incl_app_cb,[]}}
 ]}.

Erlang 运行时系统启动后,需要一些进程来与应用程序进行交互,它们注册为程序控制器进程 application_controller,就像是 Kernel application 核心进程的一部分。应用程序可以进行四种基本操作,loaded, unloaded, started, stopped

示例,Supervision Tree 框架应用程序回调模块:

ch_sup.erl 模块:

-module(ch_sup).
-behaviour(supervisor).

-compile([export_all,nowarn_export_all]).

% -export([start_link/0]).
% -export([init/1]).

start_link() ->
    supervisor:start_link(ch_sup, []).

init(_Args) ->
    SupFlags = #{strategy => one_for_one, intensity => 1, period => 5},
    ChildSpecs = [#{id => ch3,
                    start => {ch3, start_link, []},
                    restart => permanent,
                    shutdown => brutal_kill,
                    type => worker,
                    modules => [cg3]}],
    {ok, {SupFlags, ChildSpecs}}.

ch_app.erl 模块:

-module(ch_app).
-behaviour(application).

-export([start/2, stop/1]).

start(_Type, _Args) ->
    ch_sup:start_link().

stop(_State) ->
    ok.

ch_app.app 应用程序资源文件:

{application, ch_app,
 [{description, "Channel allocator"},
  {vsn, "1"},
  {modules, [ch_app, ch_sup, ch3]},
  {registered, [ch3]},
  {applications, [kernel, stdlib, sasl]},
  {mod, {ch_app,[]}},
  {env, [{file, "/usr/local/log"}]}
 ]}.

加载应用程序 application:load 需要用到 .app 文件,文件名必需和指定的 atom 程序名相同,每个选项都是 {Key,Value} 元组键值对。其中 mod 主键指定的值就是应用程序启动时的参数,这里指定的是 ch_app 和 []。这里指定的 env 这些配置可以使用 application 模块的 get_envget_all_keyget_key 方法获取相应数据。

在发布应用程序时,可以使用 Erlang/OTP 提供的打包工具 systools 生成应用程序资源文件。它生成的文件包含以下这些主键 description, vsn, modules, registered, applications

其中 applications 主键设置了应用程序的依赖项,kernel、stdlib、sasl 是程序的基础依赖,默认前两者是加载的。注意,如果依赖模块没有运行,是不能够运行 Application 的:

1> application:start(ch_app).
{error,{not_started,sasl}}
2> application:start(sasl).
ok

一个程序停止后,卸载后,或者根本没有开始,它就会从 Erlang 内部的应用控制器数据库中移除:

1> application:load(ch_app).
ok
2> application:loaded_applications().
[{kernel,"ERTS  CXC 138 10","2.8.1.3"},
 {stdlib,"ERTS  CXC 138 10","1.11.4.3"},
 {ch_app,"Channel allocator","1"}]

3> application:start(ch_app).
ok
4> application:get_env(ch_app, file).
{ok,"/usr/local/log"}

5> application:unload(ch_app).
ok
6> application:loaded_applications().
[{kernel,"ERTS  CXC 138 10","2.8.1.3"},
 {stdlib,"ERTS  CXC 138 10","1.11.4.3"}]

7> application:which_applications().
[{stdlib,"ERTS  CXC 138 10","3.9"},
 {kernel,"ERTS  CXC 138 10","6.4"}]

9> application:get_all_key(ch_app).
{ok,[{description,"Channel allocator"},
     {id,[]},
     {vsn,"1"},
     {modules,[ch_app,ch_sup,ch3]},
     {maxP,infinity},
     {maxT,infinity},
     {registered,[ch3]},
     {included_applications,[]},
     {applications,[kernel,stdlib,sasl]},
     {env,[{file,"/usr/local/log"}]},
     {mod,{ch_app,[]}},
     {start_phases,undefined}]}

可以指定配置运行程序,假设内容 test.config 配置内容 [{ch_app, [{file, "testlog"}]}].,注意有句点:

% erl -config test
Erlang (BEAM) emulator version 5.2.3.6 [hipe] [threads:0]

Eshell V5.2.3.6  (abort with ^G)
1> application:start(ch_app).
ok
2> application:get_env(ch_app, file).
{ok,"testlog"}

如果使用了 Release Handling 还可以配置 sys.config,包括 .app 文件的配置所有配置都可以通过命令行指定:

% erl -ApplName Par1 Val1 ... ParN ValN

Example:

% erl -ch_app file '"testlog"'
Erlang (BEAM) emulator version 5.2.3.6 [hipe] [threads:0]

Eshell V5.2.3.6  (abort with ^G)
1> application:start(ch_app).
ok
2> application:get_env(ch_app, file).
{ok,"testlog"}

应用程序启动时,可以指定启动类型,永久方式 permanent 或临时方式 transient:

默认是临时方式,永久方式启动的应用程序终止意味着运行时系统也终止。

临时应用程序可以是正常终止 normal,也可以是其它反常终止 abnormally,这时其它程序和运行时系统也终止,并产生一个 normal 以外的 Reason。如果是终结 terminates,那么其它应用程序不会终止。

应用程序总是可以通过 application:stop/1 函数结束,不管什么运行模式,并且不影响其它应用程序。

临时应用程序在实践中少见,因为,Supervision Tree 结束时产的 Reason 是 shutdown 而不是 normal

上一篇 下一篇

猜你喜欢

热点阅读