应用程序有两个服务器:一个质数服务器和一个面积服务器。
质数服务器
这里是质数服务器的代码,它是用gen_server行为编写的:
-module(prime_server).
%%%=======================EXPORT=======================
-export([new_prime/1, start_link/0]).
%% gen_server callbacks
-behaviour(gen_server).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
%%%=======================INCLUDE======================
%%%=======================RECORD=======================
-record(state, {}).
%%%=======================DEFINE=======================
%%%=======================TYPE=========================
%%%=================EXPORTED FUNCTIONS=================
%% ----------------------------------------------------
%% Description: Starts the server
%% ----------------------------------------------------
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
new_prime(N) ->
%% 20000(ms)是一个超时设置
gen_server:call(?MODULE, {prime, N}, 20000).
%%%===============gen_server callbacks=================
%% ----------------------------------------------------
%% Description: Initializes the server
%% ----------------------------------------------------
init([]) ->
process_flag(trap_exit, true),
io:format("~p starting~n", [?MODULE]),
{ok, 0}.
%% ----------------------------------------------------
%% Description: Handling call messages
%% ----------------------------------------------------
handle_call({prime, K}, _From, N) ->
{reply, make_new_prime(K), N + 1}.
%% ----------------------------------------------------
%% Description: Handling cast messages
%% ----------------------------------------------------
handle_cast(_Msg, N) ->
{noreply, N}.
%% ----------------------------------------------------
%% Description: Handling all non call/cast messages
%% ----------------------------------------------------
handle_info(_Info, N) ->
{noreply, N}.
%% ----------------------------------------------------
%% Description:
%% ----------------------------------------------------
terminate(_Reason, _N) ->
io:format("~p stopping ~n", [?MODULE]),
ok.
%% ----------------------------------------------------
%% Description: Convert process state when code is changed
%% ----------------------------------------------------
code_change(_OldVsn, N, _Extra) ->
{ok, N}.
make_new_prime(K) ->
if
K > 100 ->
alarm_handler:set_alarm(tooHot),
N = lib_primes:make_prime(K),
alarm_handler:clear_alarm(tooHot),
N;
true ->
lib_primes:make_prime(K)
end.
%%%===================LOCAL FUNCTIONS==================
%% ----------------------------------------------------
%% Description:
%% ----------------------------------------------------
面积服务器
现在轮到面积服务器了,它也是用gen_server行为编写的。在编写这个示例时剪切粘贴了质数服务器里的代码,然后把它转变成面积服务器。用这种方式编写服务器速度极快。
-module(area_server).
%%%=======================EXPORT=======================
-export([area/1, start_link/0]).
%% gen_server callbacks
-behaviour(gen_server).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
%%%=======================INCLUDE======================
%%%=======================RECORD=======================
-record(state, {}).
%%%=======================DEFINE=======================
%%%=======================TYPE=========================
%%%=================EXPORTED FUNCTIONS=================
%% ----------------------------------------------------
%% Description: Starts the server
%% ----------------------------------------------------
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
area(Thing) ->
gen_server:call(?MODULE, {area, Thing}).
%%%===============gen_server callbacks=================
%% ----------------------------------------------------
%% Description: Initializes the server
%% ----------------------------------------------------
init([]) ->
%% 如果想让terminate/2在应用程序停止时被调用,就必须设置trap_exit = true
process_flag(trap_exit, true),
io:format("~p starting~n", [?MODULE]),
{ok, 0}.
%% ----------------------------------------------------
%% Description: Handling call messages
%% ----------------------------------------------------
handle_call({area, Thing}, _From, N) ->
{reply, compute_area(Thing), N + 1}.
%% ----------------------------------------------------
%% Description: Handling cast messages
%% ----------------------------------------------------
handle_cast(_Request, N) ->
{noreply, N}.
%% ----------------------------------------------------
%% Description: Handling all non call/cast messages
%% ----------------------------------------------------
handle_info(_Info, N) ->
{noreply, N}.
%% ----------------------------------------------------
%% Description:
%% ----------------------------------------------------
terminate(_Reason, _State) ->
io:format("~o stopping~n", [?MODULE]),
ok.
%% ----------------------------------------------------
%% Description: Convert process state when code is changed
%% ----------------------------------------------------
code_change(_OldVsn, N, _Extra) ->
{ok, N}.
compute_area({square, X}) -> X * X;
compute_area({rectongle, X, Y}) -> X * Y.
%%%===================LOCAL FUNCTIONS==================
%% ----------------------------------------------------
%% Description:
%% ----------------------------------------------------
编写完了应用程序代码,还加了一个小错误。现在必须设立一个监控结构来检测和纠正所有可能在运行时发生的错误。
监控树
监控树是一种由进程组成的树形结构。树的上级进程(监控器)监视着下级进程(工作器),如果下级进程挂了就会重启它们。监控树有两种:
一对一监控树
在一对一监控里,如果某个工作器崩溃了,就会被监控器重启。
一对多监控树
在一对多监控里,如果任何一个工作器崩溃了,所有工作进程都会被终止(通过调用相应回调模块里的terminate/2
函数)然后重启。
监控器是用
OTP supervisor
行为创建的。这个行为用一个回调模块作为参数,里面指定了监控策略以及如何启动监控树里的各个工作进程。监控树通过以下形式的函数指定:
init(...) ->
{ok,
{RestartStrategy, MaxRestarts, Time},
[Worker1, Worker2, ...]
}}.
这里的
RestartStrategy
是原子
one_for_one
或
one_for_all
,
MaxRestarts
和
Time
则指定“重启频率”。如果一个监控器在Time
秒内执行了超过
MaxRestarts
次重启,那么这个监控器就会终止所有工作进程然后退出。这是为了防止出现一种情形,即某个进程崩溃、被重启,然后又因为相同原因崩溃而形成的无限循环。
而其中Worker1
和
Worker2
这些是描述如何启动各个工作进程的元组。