zoukankan      html  css  js  c++  java
  • 启动erlang/OTP里面的Web服务器(application INETS启动过程代码分析)

    上两篇分别用两种方式启动了inets中的httpd,其实本质一样的;下面简单分析一下过程,函数粒度的介绍。

    1,下面是application inets的代码目录,虽然ftp、tftp、http_client、http_lib、http_server、inets_app在这目录中并列,其实inets_app扮演顶层控制角色;

    只有inets_app是一个application,而其他都是module---application的一部分并且需要application启动和管理。

    [root@james src]# pwd
    /root/otp_src_17.5/lib/inets/src
    [root@james src]# l
    total 32
    drwxrwxr-x 2 2004 uucp 4096 Mar 31 20:32 ftp
    drwxrwxr-x 2 2004 uucp 4096 Mar 31 20:32 http_client
    drwxrwxr-x 2 2004 uucp 4096 Mar 31 20:32 http_lib
    drwxrwxr-x 2 2004 uucp 4096 Mar 31 20:32 http_server
    drwxrwxr-x 2 2004 uucp 4096 Mar 31 20:32 inets_app
    -rw-rw-r-- 1 2004 uucp 1083 Mar 31 20:32 Makefile
    -rw-rw-r-- 1 2004 uucp  126 Mar 31 20:32 subdirs.mk
    drwxrwxr-x 2 2004 uucp 4096 Mar 31 20:32 tftp
    [root@james src]#

    2,下面是inets_app的目录和文件:

    [root@james inets_app]# l
    total 76
    -rw-rw-r-- 1 2004 uucp   838 Mar 31 20:32 inets_app.erl
    -rw-rw-r-- 1 2004 uucp  3216 Mar 31 20:32 inets.app.src
    -rw-rw-r-- 1 2004 uucp   812 Mar 31 20:32 inets.appup.src
    -rw-rw-r-- 1 2004 uucp   126 Mar 31 20:32 inets.config
    -rw-rw-r-- 1 2004 uucp 15651 Mar 31 20:32 inets.erl
    -rw-rw-r-- 1 2004 uucp  1591 Mar 31 20:32 inets_internal.hrl
    -rw-rw-r-- 1 2004 uucp  1215 Mar 31 20:32 inets.mk
    -rw-rw-r-- 1 2004 uucp 11503 Mar 31 20:32 inets_regexp.erl
    -rw-rw-r-- 1 2004 uucp  2217 Mar 31 20:32 inets_service.erl
    -rw-rw-r-- 1 2004 uucp  3870 Mar 31 20:32 inets_sup.erl
    -rw-rw-r-- 1 2004 uucp 11277 Mar 31 20:32 inets_trace.erl
    -rw-rw-r-- 1 2004 uucp  3256 Mar 31 20:32 Makefile
    [root@james inets_app]#

    以下几个文件比较重要,控制着整个application的启动停止:

    • inets_app.erl:behaviour是application,其中定义启动函数start/2;
    • inets_sup.erl:behaviour是supervisor,其中定义start_link/0和回调函数init/1;
    • inets.erl:是一个模块,实现了几个关于application的启动、停止、服务信息、trace开关控制的函数;
    • inet.app.src:是application inets的配置文件,重要!

    3,简单说说inets.erl,这里没有复杂的东西也不是最重要的:

     41 %%--------------------------------------------------------------------
     42 %% Function: start([, Type]) -> ok
     43 %%
     44 %%  Type =  permanent | transient | temporary
     45 %%
     46 %% Description: Starts the inets application. Default type
     47 %% is temporary. see application(3)
     48 %%--------------------------------------------------------------------
     49 start() ->
     50     application:start(inets).
     51
     52 start(Type) ->
     53     application:start(inets, Type).

    启动函数start,本质调用了inets_app.erl中的代码;

     91 %%--------------------------------------------------------------------
     92 %% Function: stop() -> ok
     93 %%
     94 %% Description: Stops the inets application.
     95 %%--------------------------------------------------------------------
     96 stop() ->
     97     application:stop(inets).
     98
    ......
    108 stop(stand_alone, Pid) ->
    109     true = exit(Pid, shutdown),
    110     ok;
    111
    112 stop(Service, Pid) ->
    113     Module = service_module(Service),
    114     call_service(Module, stop_service, Pid).

    停止函数stop,也是调用了inets_app.erl中的函数,或者使用内嵌函数exit shutdown进程。(这里略过)

    3+,这里解释一下:调用application:start(inets)和application:stop(inets),会发生什么?这个得听从inets.app.src配置文件的指示!先看配置文件:

    {application,inets,
     [{description, "INETS  CXC 138 49"},
      {vsn, "%VSN%"},
      {modules,[
                inets,
                inets_sup,
                inets_app,
                inets_service,                      
                inets_regexp,                      
                inets_trace,                      
    
                %% FTP
                ftp,
                ......
    
                %% HTTP server:
                httpd,
                httpd_acceptor,
                httpd_acceptor_sup,
                httpd_cgi,
                httpd_connection_sup,
                httpd_conf,
                httpd_custom,
    
                ......
                tftp_logger,
                tftp_sup
            ]},
      {registered,[inets_sup, httpc_manager]},
      %% If the "new" ssl is used then 'crypto' must be started before inets.
      {applications,[kernel,stdlib]},
      {mod,{inets_app,[]}},
      {runtime_dependencies, ["stdlib-2.0","ssl-5.3.4","runtime_tools-1.8.14",
                  "mnesia-4.12","kernel-3.0","erts-6.0"]}]}.

    配置文件中的mod,指示了下一棒交给谁------inet_app.erl!

    实际上inets_app.erl里真的实现了一个回调函数start/2,继续把事情做下去。

    4,接下来说inets_app.erl的实现:

     26 start(_Type, _State) ->
     27     inets_sup:start_link().
     28
     29 stop(_State) ->
     30     ok.

    实现很简单,就是调用inets_sup.erl中的函数。

    5, 下面说inets_sup.erl中的函数start_link函数和回调函数init:

     34 %%%=========================================================================
     35 %%%  External functions
     36 %%%=========================================================================
     37 start_link() ->
     38     supervisor:start_link({local, ?MODULE}, ?MODULE, []).
     39
     40 %%%=========================================================================
     41 %%%  Supervisor callback
     42 %%%=========================================================================
     43 init([]) ->
     44     SupFlags = {one_for_one, 10, 3600},
     45     Children = children(),
     46     {ok, {SupFlags, Children}}.
     47

    inets_sup.erl的behaviour自然是supervisor,通过supervisor调用start_link时,init作为回调函数(supervisor的设计就是这样)。

    SupFlags,是supervisor behaviour的配置参数,在这里就是一个Tuple结构 ------ {one_for_one, 10, 3600},此处略过(OTP相关文档中有介绍supervisor)。

    5+,下面详细看children函数,这里信息量最丰富!

     51 get_services() ->
     52     case (catch application:get_env(inets, services)) of
     53         {ok, Services} ->
     54             Services;
     55         _ ->
     56             []
     57     end.
     58
     59 children() ->
     60     Services = get_services(),
     61     HttpdServices = [Service || Service <- Services, is_httpd(Service)],
     62     HttpcServices =  [Service || Service <- Services, is_httpc(Service)],
     63     TftpdServices =  [Service || Service <- Services, is_tftpd(Service)],
     64     [ftp_child_spec(), httpc_child_spec(HttpcServices),
     65      httpd_child_spec(HttpdServices), tftpd_child_spec(TftpdServices)].
     66

    做个实验,这里很有收获:

    原来启动erlang虚拟机时-config inets_httpd18080.config已经被解析(这个解析过程我还没分析,有时间在搞一篇吧),并且从这里获取到。

    这样看来,children函数中Services是一个List结构,而HttpdServices也是一个List结构------列表元素是Tuple结构。

    如果httpd服务只有一个配置文件,那么HttpdServices形如[{httpd, "root/inetsConf/18080.conf"}];如果还有关于https的配置,那么就像这样:[{httpd, "/root/inetsConf/18080.conf"}, {httpd, "/root/inetsConf/https_18443.conf"}]~

    下面看函数httpd_child_spec。

    5++,再看httpd_child_spec函数的代码:

     87 httpd_child_spec(HttpdServices) ->
     88     Name = httpd_sup,
     89     StartFunc = {httpd_sup, start_link, [HttpdServices]},
     90     Restart = permanent,
     91     Shutdown = infinity,
     92     Modules = [httpd_sup],
     93     Type = supervisor,
     94     {Name, StartFunc, Restart, Shutdown, Type, Modules}.
     95

    其中StartFunc是supervisor启动worker时需要的Tuple结构,这里HttpdServices作为StartFunc的一个部分。

    可以解释一下,httpd_sup是模块,start_link是这个模块的函数,而[HttpdServices]作为这个函数的参数;

    很明显,这里并不解析"/root/inetsConf/18080.conf"配置文件,仅作为参数传下去,进入了具体的module(比如http_server)~

    6,简单列一下http_server中httpd_sup.erl实现解析参数的代码------使用proplist把文件读了再解析再存储以便后面步骤检索判断,看起来很明白,我就不废话解释了:(从回调函数init/1开始)

     89 %%%=========================================================================
     90 %%%  Supervisor callback
     91 %%%=========================================================================
     92 init([HttpdServices]) ->
     93     ?hdrd("starting", [{httpd_service, HttpdServices}]),
     94     RestartStrategy = one_for_one,
     95     MaxR = 10,
     96     MaxT = 3600,
     97     Children = child_specs(HttpdServices, []),
     98     {ok, {{RestartStrategy, MaxR, MaxT}, Children}}.
     99
    ......
    118 child_specs([], Acc) ->
    119     Acc;
    120 child_specs([{httpd, HttpdService} | Rest], Acc) ->
    121     ?hdrd("child specs", [{httpd, HttpdService}]),
    122     NewHttpdService = (catch mk_tuple_list(HttpdService)),
    123     ?hdrd("child specs", [{new_httpd, NewHttpdService}]),
    124     case catch child_spec(NewHttpdService) of
    125         {error, Reason} ->
    126             ?hdri("failed generating child spec", [{reason, Reason}]),
    127             error_msg("Failed to start service: ~n~p ~n due to: ~p~n",
    128                       [HttpdService, Reason]),
    129             child_specs(Rest, Acc);
    130         Spec ->
    131             ?hdrt("child spec", [{child_spec, Spec}]),
    132             child_specs(Rest, [Spec | Acc])
    133     end.
    134
    135 child_spec(HttpdService) ->
    136     {ok, Config}  = httpd_config(HttpdService),
    137     ?hdrt("child spec", [{config, Config}]),
    138     Debug         = proplists:get_value(debug, Config, []),
    139     AcceptTimeout = proplists:get_value(accept_timeout, Config, 15000),
    140     httpd_util:valid_options(Debug, AcceptTimeout, Config),
    141     httpd_child_spec(Config, AcceptTimeout, Debug).
    142
    143 httpd_config([Value| _] = Config) when is_tuple(Value) ->
    144     case proplists:get_value(file, Config) of
    145         undefined ->
    146             case proplists:get_value(proplist_file, Config) of
    147                 undefined ->
    148                     httpd_conf:validate_properties(Config);
    149                 File ->
    150                    try file:consult(File) of
    151                        {ok, [PropList]} ->
    152                            httpd_conf:validate_properties(PropList)
    153                    catch
    154                        exit:_ ->
    155                            throw({error,
    156                                   {could_not_consult_proplist_file, File}}) 
    157                    end
    158             end;
    159         File ->
    160             {ok, File}
    161     end.
    162
    163 httpd_child_spec([Value| _] = Config, AcceptTimeout, Debug)
    164   when is_tuple(Value)  ->
    165     ?hdrt("httpd_child_spec - entry", [{accept_timeout, AcceptTimeout},
    166                                        {debug,          Debug}]),
    167     Address = proplists:get_value(bind_address, Config, any),
    168     Port    = proplists:get_value(port, Config, 80),
    169     httpd_child_spec(Config, AcceptTimeout, Debug, Address, Port);
    170
    171 %% In this case the AcceptTimeout and Debug will only have default values...
    172 httpd_child_spec(ConfigFile, AcceptTimeoutDef, DebugDef) ->
    173     ?hdrt("httpd_child_spec - entry", [{config_file,        ConfigFile},
    174                                        {accept_timeout_def, AcceptTimeoutDef    },
    175                                        {debug_def,          DebugDef}]),
    176     case httpd_conf:load(ConfigFile) of
    177         {ok, ConfigList} ->
    178             ?hdrt("httpd_child_spec - loaded", [{config_list, ConfigList}]),
    179             case (catch httpd_conf:validate_properties(ConfigList)) of
    180                 {ok, Config} ->
    181                     ?hdrt("httpd_child_spec - validated", [{config, Config}]    ),
    182                     Address = proplists:get_value(bind_address, Config, any)    ,
    183                     Port    = proplists:get_value(port, Config, 80),
    184                     AcceptTimeout =
    185                         proplists:get_value(accept_timeout, Config,
    186                                             AcceptTimeoutDef),
    187                     Debug   =
    188                         proplists:get_value(debug, Config, DebugDef),
    189                     httpd_child_spec([{file, ConfigFile} | Config],
    190                                      AcceptTimeout, Debug, Address, Port);
    191                 Error ->
    192                     Error
    193             end;
    194         Error ->
    195             Error
    196     end.
    197
    198 httpd_child_spec(Config, AcceptTimeout, Debug, Addr, Port) ->
    199     Fd  = proplists:get_value(fd, Config, undefined),
    200     case Port == 0 orelse Fd =/= undefined of
    201         true ->
    202             httpd_child_spec_listen(Config, AcceptTimeout, Debug, Addr, Port    );
    203         false ->
    204             httpd_child_spec_nolisten(Config, AcceptTimeout, Debug, Addr, Po    rt)
    205     end.
  • 相关阅读:
    让owncloud成为微博式记事本
    软件即抽象
    十年,最后一个alpha,0.4.1版的reactos终于变得可赏可玩了
    使用群晖作mineportalbox(1):合理且不折腾地使用群晖硬件和套件
    使用群晖作mineportalbox(2):把webstation打造成snippter空间
    使用群晖作mineportalbox(3):在阿里云上单盘安装群晖skynas
    统一的分布式数据库和文件系统mongodb,及其用于解决aliyun上做站的存储成本方案
    吃桃
    1020: C语言程序设计教程(第三版)课后习题6.9
    分数序列
  • 原文地址:https://www.cnblogs.com/andypeker/p/4662874.html
Copyright © 2011-2022 走看看