码迷,mamicode.com
首页 > 移动开发 > 详细

启动erlang/OTP里面的Web服务器(application INETS启动过程代码分析)

时间:2015-07-21 01:01:54      阅读:206      评论:0      收藏:0      [点我收藏+]

标签:

 

上两篇分别用两种方式启动了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开关的控制函数。

 

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进程。(这里略过)

 

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函数中HttpdServices就是一个Tuple结构,比如{httpd, "/root/inetsConf/18080.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把文件读了再解析再存储以便后面步骤检索判断,看起来很明白,我就不废话解释了:

 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.

 

启动erlang/OTP里面的Web服务器(application INETS启动过程代码分析)

标签:

原文地址:http://www.cnblogs.com/andypeker/p/4662874.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!