erlang应用管理者application_controller

application_controller是系统最早启动的process之一,也是非分布式application的管理所在

1. application_controller是什么?

The application_controller controls local applications only.  
A local application can be loaded/started/stopped/unloaded and changed. 
The control of distributed applications is taken care of by another process (default is dist_ac).

简单的说这个process(application_controller)就是控制非分布式application的真正所在

2.application_controller什么时候启动?

2.1 otp-20中 application_controller 启动顺序
$ erl -init_debug

{progress,preloaded}
{progress,kernel_load_completed}
{progress,modules_loaded}
{start,heart}
{start,error_logger}
{start,application_controller}
{progress,init_kernel_started}
...

是继heart,error_logger之后启动,而且在kernel启动之前,因为kernel也是一个application

2.2 otp-21中 application_controller 启动顺序
$ erl -init_debug
{progress,preloaded}
{progress,kernel_load_completed}
{progress,modules_loaded}
{start,heart}
{start,logger}
{start,application_controller}
{progress,init_kernel_started}
...

由于在otp-21中,将error_logger 改为logger

不管怎么说,application_controller启动的顺序都在kernel之前,级别和init相当

3. application 其实是 application_controller 的代理(proxy)

application 的一般操作,实际上都是调用application_controller,上代码

% start/2
start(Application, RestartType) ->
    case load(Application) of
    ok ->
        Name = get_appl_name(Application),
        application_controller:start_application(Name, RestartType);
    {error, {already_loaded, Name}} ->
        application_controller:start_application(Name, RestartType);
    Error ->
        Error
    end.
    
% start_boot/2
start_boot(Application, RestartType) ->
    application_controller:start_boot_application(Application, RestartType).
    
% stop/1
stop(Application) ->
    application_controller:stop_application(Application).

% which_applications/0
which_applications() ->
    application_controller:which_applications().

% loaded_applications
loaded_applications() -> 
    application_controller:loaded_applications().
    
% set_env/3
set_env(Application, Key, Val) -> 
    application_controller:set_env(Application, Key, Val).
% get_env/2
get_env(Application, Key) -> 
    application_controller:get_env(Application, Key).

4.详细的分析每个行为

4.1 数据结构appl_data
-record(appl_data, {name, regs = [], phases, mod, mods = [],
                    inc_apps, maxP = infinity, maxT = infinity}).
4.2 载入application
% application_controller.erl
handle_call({load_application, Application}, From, S) ->
  case catch do_load_application(Application, S) of
    {ok, NewS} ->
      AppName = get_appl_name(Application),
      % 如果是分布式 app,就给dist_ac发送一条消息
      case cntrl(AppName, S, {ac_load_application_req, AppName}) of
        true ->
          % 如果App是一个分布式Application,那么控制权交给 dist_ac
          {noreply, S#state{loading = [{AppName, From} | S#state.loading]}};
        false ->
          {reply, ok, NewS}
      end;
    {error, _} = Error ->
      {reply, Error, S};
    {'EXIT', R} ->
      {reply, {error, R}, S}
  end;

简单的说就是从${App}.app构建#appl_data{}

如果这个 app是分布式的,那么就给dist_ac发送消息{ac_load_application_req,App} 等待dist_ac返回数据

4.2 启动 application
% 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,
  case lists:keyfind(AppName, 1, Start_req) of
    false ->
      case catch check_start_cond(AppName, RestartType, Started, Running) of
        {ok, Appl} ->
          Cntrl = cntrl(AppName, S, {ac_start_application_req, AppName}),
          Perm = application:get_env(kernel, permissions),
          case {Cntrl, Perm} of
            {true, _} ->
              % 控制权交给 dist_ac
              {noreply, S#state{...}};
            {false, undefined} ->
              % 非分布式应用,就直接启动了
              spawn_starter(From, Appl, S, normal),
              {noreply, S#state{...}};
            {false, {ok, Perms}} ->
              case lists:member({AppName, false}, Perms) of
                false ->
                % 在权限允许分为内,也直接启动了
                  spawn_starter(From, Appl, S, normal),
                  {noreply, S#state{...};
                true ->
                  % 不在权限允许内,不能启动,默认返回ok
                  SS = S#state{...},
                  {reply, ok, SS}
              end
          end;
        {error, _R} = Error ->
          {reply, Error, S}
      end;
    {AppName, _FromX} ->
      % 已经有一个请求了,同一个app不能启动2次
      SS = S#state{...},
      {noreply, SS}
  end;

简单的说,分布式application控制权交给dist_ac,非分布式application,就直接启动了
还有一点application_controllerapplication并不是直接link在一起的,就拿kernel举例,他们之间还有两层代理.拓扑图如下

|application_controller|
      |(link)
|application_master:main_loop/2|% 控制该 application最多运行时长(maxT)
      |(link)
|application_master:loop_it/4|% 控制application的生命周期以及各种回调 start/2,stop/2,pre_stop/1
      |(link)
|kernel_sup|

4.3 start_boot_application

start_application 不同的是,这个调用来自生成的boot文件,逻辑与start_applicaton一样

4.4 停止application

% 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),
          % 不是自己管理的还是得交给 dist_ac
          cntrl(AppName, S, {ac_application_stopped, AppName}),
          {reply, ok, S#state{started = NStarted}};
        false ->
          {reply, {error, {not_started, AppName}}, S}
      end
  end;

与之前start_application相同,只停止自己管理的app,分布式app还是交给dist_ac

5.总结

  1. application_controller 是最早启动的线程之一
  2. application_controller 才是其实处理启动app,停止appprocess
  3. application_controller 在遇到分布式app的时候会将控制权交给dist_ac

6. 参考文献

  1. https://github.com/erlang/otp/blob/master/lib/kernel/src/application_controller.erl
  2. https://github.com/erlang/otp/blob/master/lib/kernel/src/application_master.erl
  3. https://github.com/erlang/otp/blob/master/lib/kernel/src/application_starter.erl
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容