啓動erlang/OTP裏面的Web服務器(application INETS啓動過程代碼分析)

式啓動了inets中的httpd,下面簡單分析一下過程,函數粒度的介紹。app

 

1,下面是application inets的代碼目錄,雖然ftp、tftp、http_client、http_lib、http_server、inets_app在這目錄中並列,其實inets_app扮演頂層控制角色;函數

只有inets_app是一個application,而其餘都是module---application的一部分而且須要application啓動和管理。this

複製代碼

[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的目錄和文件:spa

 

複製代碼

[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的啓動中止:debug

  • inets_app.erl:behaviour是application,其中定義啓動函數start/2;設計

  • inets_sup.erl:behaviour是supervisor,其中定義start_link/0和回調函數init/1;code

  • inets.erl:是一個模塊,實現了幾個關於application的啓動、中止、服務信息、trace開關控制的函數;regexp

  • inet.app.src:是application inets的配置文件,重要!orm

 

3,簡單說說inets.erl,這裏沒有複雜的東西也不是最重要的:server

複製代碼

 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, Port)
205     end.

複製代碼

相關文章
相關標籤/搜索