erlang component application
erlang component application
Most of the third-party libraries we use daily are application
, so it is very important and practical for us to understand and master some features of applicaiton
.
1. What is applicaiton?, why use application
The official explanation is as follows:
When you have written code implementing some specific functionality you might want to make the code into an application, that is, a component that can be started and stopped as a unit, and which can also be reused in other systems.
http://erlang.org/doc/design_principles/applications.html
In my understanding, there are actually 2 points:
When you have written code implementing some specific functionality you might want to make the code into an application, that is, a component that can be started and stopped as a unit, and which can also be reused in other systems.
http://erlang.org/doc/design_principles/applications.html
In our daily life, there are few scenarios where a single project has multiple applications. We usually write the most
module
, to be precise, the callback module Callback Module
, and there will also be a small number of process status modules Residence Module
. The former is only A callback function switches from one state to another state, so the life cycle is only in one process
, and rarely deals with the outside world process
, so use link,monitor
These inter-process relationships are relatively rare. In contrast application
, there are many aspects to consider, not only whether the state is correct or not, but also whether the process is running abnormally or not, and even how to design the monitoring tree to keep the program robust.2. How to implement an application by yourself?
2.1 Directory structure
─ ${application}
├── doc
│ ├── internal
│ ├── examples
│ └── src
├── include
├── priv
├── src
│ └── ${application}.app.src
└── test
src
Required, store source code (.erl) priv
Not necessary, store custom files, such as nif so files, etc., and resource files include
Optional, store some header files (hrl) for easy access by other applications doc
Optional, store some documents test
optional, test files, eunit common_test
files are all placed here 2.2 application callback module (callback module)
By default it is
$APP_NAME_app
, of course, you can also define it yourself in ${application}.app.src
, the definition method is as follows:{application, $APP_NAME,
[
{description, ""},
{vsn, "1"},
{registered, []},
{applications, [
kernel,
stdlib
]},
%
%
{mod, {$CALLBACL_MODULE, Args}},
{env, []}
]}.
Let me make a simplest example, and then analyze the process of the source code, the name of applicaton
is chapp
% :chapp.app.src
{application, chapp,
[
{description, ""},
{vsn, "1"},
{registered, []},
{applications, [
kernel,
stdlib
]},
{mod, { chapp_app, [myargs]}},
{env, []}
]}.
% :chapp_app,erl
-module(chapp_app).
-behaviour(application).
-export([start/2, stop/1]).
-record(state, {
mod
}).
%%
start(_StartType, _StartArgs) ->
io:format("~p,~p,~p~n", [?MODULE, ?FUNCTION_NAME, {_StartType, _StartArgs}]),
{ok, Pid} = chapp_sup:start_link(),
{ok, Pid, #state{mod = ?MODULE}}.
%%
stop(_State) ->
io:format("~p,~p,~p~n", [?MODULE, ?FUNCTION_NAME, _State]),
ok.
The results are as follows:Eshell V10.4 (abort with ^G)
1> application:start
start/1 start/2 start_boot/1 start_boot/2 start_type/0
1> application:start(chapp).
% myargs
chapp_app,start,{normal,[myargs]}
ok
2> application:stop(chapp).
% pre_stop
chapp_app,prep_stop,{state,chapp_app}
% state
chapp_app,stop,{state,chapp_app}
=INFO REPORT==== 1-Dec-2019::14:58:57.004000 ===
application: chapp
exited: stopped
type: temporary
ok
2.3 $APP_NAME.app.src file format
% application.erl(kernel)
start(Application, RestartType) ->
case load(Application) of
ok ->
% .app.src
Name = get_appl_name(Application),
application_controller:start_application(Name, RestartType);
{error, {already_loaded, Name}} ->
application_controller:start_application(Name, RestartType);
Error ->
Error
end.
load1(Application, DistNodes) ->
% application
case application_controller:load_application(Application) of
...
Else ->
Else
end.
% application_controller.erl
% application_controller.erl
load_application(Application) ->
gen_server:call(?AC, {load_application, Application}, infinity).
make_appl(Name) when is_atom(Name) ->
% path $APP_NANE.app , $APP_NAME.app.src
FName = atom_to_list(Name) ++ ".app",
case code:where_is_file(FName) of
non_existing ->
{error, {file:format_error(enoent), FName}};
FullName ->
case prim_consult(FullName) of
{ok, [Application]} ->
{ok, make_appl_i(Application)};
{error, Reason} ->
{error, {file:format_error(Reason), FName}};
error ->
{error, "bad encoding"}
end
end;
% $APP_NAME.app.src
% {application, Name, Opts}
% , :description,mod,env
% mod ,
make_appl_i({application, Name, Opts}) when is_atom(Name), is_list(Opts) ->
Descr = get_opt(description, Opts, ""),
Id = get_opt(id, Opts, ""),
Vsn = get_opt(vsn, Opts, ""),
Mods = get_opt(modules, Opts, []),
Regs = get_opt(registered, Opts, []),
Apps = get_opt(applications, Opts, []),
Mod =
case get_opt(mod, Opts, []) of
{M, _A} = MA when is_atom(M) -> MA;
[] -> [];
Other -> throw({error, {badstartspec, Other}})
end,
Phases = get_opt(start_phases, Opts, undefined),
Env = get_opt(env, Opts, []),
MaxP = get_opt(maxP, Opts, infinity),
MaxT = get_opt(maxT, Opts, infinity),
IncApps = get_opt(included_applications, Opts, []),
{#appl_data{name = Name, regs = Regs, mod = Mod, phases = Phases,
mods = Mods, inc_apps = IncApps, maxP = MaxP, maxT = MaxT},
Env, IncApps, Descr, Id, Vsn, Apps};
2.4 Application startup steps
% application_controller.erl
handle_call({start_application, AppName, RestartType}, From, S) ->
#state{running = Running, starting = Starting, start_p_false = SPF,
started = Started, start_req = Start_req} = S,
%% Check if the commandline environment variables are OK.
%% Incase of erroneous variables do not start the application,
%% if the application is permanent crash the node.
%% Check if the application is already starting.
case lists:keyfind(AppName, 1, Start_req) of
false ->
case catch check_start_cond(AppName, RestartType, Started, Running) of
{ok, Appl} ->
......
{false, undefined} ->
%
spawn_starter(From, Appl, S, normal),
{noreply, S#state{starting = [{AppName, RestartType, normal, From} |
Starting],
start_req = [{AppName, From} | Start_req]}};
......
{error, _R} = Error ->
{reply, Error, S}
end;
{AppName, _FromX} ->
SS = S#state{start_req = [{AppName, From} | Start_req]},
{noreply, SS}
end;
start_appl(Appl, S, Type) ->
ApplData = Appl#appl.appl_data,
case ApplData#appl_data.mod of
[] ->
{ok, undefined};
_ ->
%% Name = ApplData#appl_data.name,
......
% application_master
case application_master:start_link(ApplData, Type) of
{ok, _Pid} = Ok ->
Ok;
{error, _Reason} = Error ->
throw(Error)
end
end.
% application_master.erl
start_link(ApplData, Type) ->
Parent = whereis(application_controller),
proc_lib:start_link(application_master, init, [Parent, self(), ApplData, Type]).
start_it_old(Tag, From, Type, ApplData) ->
{M, A} = ApplData#appl_data.mod,
case catch M:start(Type, A) of
{ok, Pid} ->
% , State = []
link(Pid),
From ! {Tag, {ok, self()}},
loop_it(From, Pid, M, []);
{ok, Pid, AppState} ->
% , State = AppState
link(Pid),
From ! {Tag, {ok, self()}},
% , loop
loop_it(From, Pid, M, AppState);
{'EXIT', normal} ->
From ! {Tag, {error, {{'EXIT', normal}, {M, start, [Type, A]}}}};
{error, Reason} ->
From ! {Tag, {error, {Reason, {M, start, [Type, A]}}}};
Other ->
From ! {Tag, {error, {bad_return, {{M, start, [Type, A]}, Other}}}}
end.
% ,
start_supervisor(Type, M, A) ->
case catch M:start(Type, A) of
{ok, Pid} ->
{ok, Pid, []};
{ok, Pid, AppState} ->
{ok, Pid, AppState};
{error, Reason} ->
{error, {Reason, {M, start, [Type, A]}}};
{'EXIT', normal} ->
{error, {{'EXIT', normal}, {M, start, [Type, A]}}};
Other ->
{error, {bad_return, {{M, start, [Type, A]}, Other}}}
end.
The topology diagram between procs after startup is as follows:|application_controller | --- |(application_master:main_loop)| --- | (application_master:loop_it)| --- | chapp_sup|
At this point, we have walked through the code flow of the startup, and some callback parameters are also clear. Summarized in the following pointsapplication_controller
is the appcation
that actually started process
, or the parent process (process)
application
The startup is asynchronous, and the result cast
is given to application_controller
(load)
2.5 How does the application stop (stop) ?
% application_controller.erl
handle_call({stop_application, AppName}, _From, S) ->
#state{running = Running, started = Started} = S,
case lists:keyfind(AppName, 1, Running) of
{_AppName, Id} ->
{_AppName2, Type} = lists:keyfind(AppName, 1, Started),
stop_appl(AppName, Id, Type),
NRunning = keydelete(AppName, 1, Running),
NStarted = keydelete(AppName, 1, Started),
cntrl(AppName, S, {ac_application_stopped, AppName}),
{reply, ok, S#state{running = NRunning, started = NStarted}};
false ->
case lists:keymember(AppName, 1, Started) of
true ->
NStarted = keydelete(AppName, 1, Started),
cntrl(AppName, S, {ac_application_stopped, AppName}),
{reply, ok, S#state{started = NStarted}};
false ->
{reply, {error, {not_started, AppName}}, S}
end
% application_master.erl
stop(AppMaster) -> call(AppMaster, stop).
main_loop(Parent, State) ->
receive
......
Other ->
NewState = handle_msg(Other, State),
main_loop(Parent, NewState)
end.
handle_msg({stop, Tag, From}, State) ->
catch terminate(normal, State),
From ! {Tag, ok},
%
exit(normal);
loop_it(Parent, Child, Mod, AppState) ->
receive
......
{'EXIT', Parent, Reason} ->
% stop pre_stop
% application_master:main_loop , application_master:loop_it
%
NewAppState = prep_stop(Mod, AppState),
exit(Child, Reason),
receive
{'EXIT', Child, Reason2} ->
exit(Reason2)
end,
% stop
catch Mod:stop(NewAppState);
......
_ ->
......
end.
At this point, the stop logic of application
has been analyzed, and we can also find a hook:pre_stop
that is not in the document by reading the code3. Summary
This article demonstrates the implementation of
application
through examples and reading the source code, hoping to deepen the reader's understanding of application
and lay a solid foundation for the rational use of application
.4. References:
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
다양한 언어의 JSONJSON은 Javascript 표기법을 사용하여 데이터 구조를 레이아웃하는 데이터 형식입니다. 그러나 Javascript가 코드에서 이러한 구조를 나타낼 수 있는 유일한 언어는 아닙니다. 저는 일반적으로 '객체'{}...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.