OTP应用的组织形式:
应用目录的名字 <application-name>[-Version] 以及 应用目录下的子目录。
为应用添加元数据:
{application, static_world, [
{description, "Cowboy static file handler example."},
{vsn, "1"},
{modules, []},
{registered, [static_world_sup]},
{applications, [
kernel,
stdlib,
cowboy
]},
{mod, {static_world_app, []}},
{env, []}
]}.
这个.app文件的作用在于告诉OTP如何启动应用,以及该应用如何与系统中的其他应用相融合。再重复一遍,我们的首要目的并不是在于打包发布,而在于组装更大的可启动,停止,监督和升级的功能单元。
.app文件的格式很简单。除去注释,只剩下一个由句号结尾的Erlang项式:三元组{applications,…,….}. 。第二个元素是应用名称所对应的原子,此处即是static_world。第三个元素是一个参数列表,其中每个参数都是{key, Value}对的形式,有些是必需的,有些是可选的。
.app文件中的主要参数
建立好了应用的目录结构,元数据,整个引用还远未完工。Mod的描述中曾提到,你的应用还需要一个启动入口,它必须是application行为模式的一个实现模块。
应用行为模式:
每个主动应用都配有一个application行为模式的实现模块。该模块用于实现系统启动逻辑。它至少要负责根监督者的启动,该监督者将成为应用中其他所有应用进程的鼻祖。根据系统需要,应用行为模式模块还可以完成一些其他任务。
应用行为模式的命名:
应用行为模式的实现模块通常被命名为<application-name>_app。
%% @private
-module(static_world_app).
-behaviour(application). %%行为模式声明
%% API %%引用行为模式的回调函数
-export([start/2]).
-export([stop/1]).
%% API
start(_Type, _Args) ->
Dispatch = cowboy_router:compile([
{'_',[
{"/[...]", cowboy_static, {priv_dir, static_world, ""}}
]}
]),
{ok, _} = cowboy:start_http(http, 100, [{port, 8080}], [
{env, [{dispatch, Dispatch}]
]),
static_world_sup:start_link(). %%启动根监督者
stop( _State) ->
ok.
总结一下:建立一个OTP应用要做3件事情:
(1) 遵循标准目录结构
(2) 添加用于存在应用的元数据的.app文件
(3) 创建一个application行为模式实现模块,负责启动应用。
实现监督者:
根监督者行为模式模块的命名
根监督者行为模式的实现模块通常被命名为<application-name>_sup。
%% @private
-module(static_world_sup).
-behaviour(supervisor).
%%API
-export([start_link/0]).
%%supervisor
-export([init/1]).
%% API
start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []). %%启动监督者
%% supervisor
init([]) ->
Procs = [],
{ok, {{one_for_one, 10, 10}, Procs}}.
监督者启动策略:
{one_for_one, 10, 10} ,它是一个三元组{How, Max, Within},这里的How取值为one_for_one, 表示一旦有子进程退出,监督者就针对该进程,且针对该进程进行重启。该重启操作不会影响同时运行的其他进程。
Max和Within这两个值(此处分别取10和10)是相互关联的:它们共同确定了重启频率, 第一个值指定的是最大重启次数,第二个值指定的是时间片。表示监督者最多可以在10秒内重启子进程10次。一旦超过限制,监督者就会在终止所有子进程后自我了断,并顺着监督者向上汇报故障信息。这些数值的取值最大程度上取决于应用本身。因此无法在此给出一个普适的推荐值。
编写子进程规范:
子进程规范是一个用于描述监督者管理的进程的元组。对于大多数监督者而言,子进程会随监督者的启动而启动并在监督者的生命周期结束时退出。对于单个需要监督的进程,
在cowboy_sup中的子进程规范的编写如下:
Procs = [{cowboy_clock, {cowboy_clock, start_link, []},
permanent, 5000, worker, [cowboy_clock]}],
子进程规范由6个元素组成:{ID, start, Restart, Shutdown, Type, Modules}。
第一个元素ID,是一个用于在系统内部标识各规范的项式。简单起见,采用的模块名,即原子cowboy_colock。
第二项Start, 是一个用于启动进程的三元组{Module, Function, Arguments}。与调用内置函数spawn/3时一样,其中第一个元素时模块名,第二个元素时函数名,第三个元素是函数的调用参数列表。在这个例子中,监督者应调用cowboy_clock:start_link来启动子进程。
第三个参数Restart,用于指明子进程发生故障时是否需要重启。此处指定为permanent,因为你搭建的是一个需要长期运行的服务,无论出于任何原因导致进程终止都应该重启进程。(该选项还可取值为表示永不重启进程的temporary),以及仅在进程意外终止时重启进程的transient。
第四个参数Shutdown,用于指明如何终止进程。此处取值为一个整数(5000),表示终止进程时应采用软关闭策略,给进程留出一段自我了断的时间(以毫秒为单位),如果进程未能在指定时间内自行退出,将被无条件终止。该选项还可取值为brutal_kill,表示在关闭监督进程时立即终止子进程;以及infinity,主要用于子进程本身也同为监督者的情况,表示应给予子进程充分的时间进行自行退出。
第五个值Type,用于表示进程是监督者(supervisor)还是工作者(worker)。在整个监督树中,除了实现了supervisor行为模式的监督者进程以外,剩下的都是工作进程。随着应用的复杂度的提升,你可以按自己的喜好组织监督进程进而形成层级结构,以提供更细粒度的控制。监督者可以通过Type字段识别子进程是否同为监督者。显然此处的服务器进程是工作进程。
第六个选项列出了该进程所依赖的模块。这部分信息仅用于在代码热升级时告知系统改以何种顺序升级整个模块。一般来说,只需列出子进程的主模块,在这里是cowboy_clock。
这样一步步来,OTP简单的应用框架就形成了。似乎还缺少自己的一些思考和整理……