今天下班比较早,吃完饭继续跟大家分享mochiweb源码。上一篇,我们看完了 mochiweb_example_deps:ensure/0,接下来回到mochiweb_example:start/0,代码如下:
%% @spec start() -> ok %% @doc Start the mochiweb_example server. start() -> mochiweb_example_deps:ensure(), ensure_started(crypto), ensure_started(mochiweb_example).
继续往下看,剩下两行代码也很简单,一个是启动crypto应用,一个是启动mochiweb_example应用,当启动这个应用时,Erlang内部会调用:mochiweb_example_app:start/2 函数,如果你不理解为什么会如此,请参照 Erlang OTP 设计原理。
接下来我们看下mochiweb_example_app:start/2 函数:
%% @spec start(_Type, _StartArgs) -> ServerRet %% @doc application start callback for mochiweb_example. start(_Type, _StartArgs) -> mochiweb_example_deps:ensure(), mochiweb_example_sup:start_link().
这里再次调用 mochiweb_example_deps:ensure/1 函数,其实这里没必要,大家还记得上一篇的 mochiweb_example_deps:new_siblings/1函数吗,其实这里最后是会过滤已经添加到 code path中的目录。这里注释掉任何一个地方,代码还是依然可以正确运行的。
mochiweb_example_deps 这个模块的作用就是实现代码搜索路径,让新创建的mochiweb_example能够很容易的运行,因为这个例子需要依赖deps下的mochiweb源码,但是这里重复调用了2次,具体我也不清楚什么原因,我们可以去掉其中一处,这样也是不影响代码运行的。
如果你想加深对代码搜索路径的理解,可以参考《Erlang编程指南》第8章,有详细介绍了;以及《Erlang程序设计》第6章,第84页,“为文件加载器设定搜索路径”,这部分的相关知识。
好了,弄清楚了这个模块的作用,我们继续往下看吧,这里调用 mochiweb_example_sup:start_link/0 方法,代码如下:
% @spec start_link() -> ServerRet %% @doc API for starting the supervisor. start_link() -> supervisor:start_link({local, ?MODULE}, ?MODULE, []).
这是个很标准的监督进程模块,不解释,直接看 mochiweb_example_sup:init/1,代码如下:
%% @spec init([]) -> SupervisorTree %% @doc supervisor callback. init([]) -> Web = web_specs(mochiweb_example_web, 8080), Processes = [Web], Strategy = {one_for_one, 10, 10}, {ok, {Strategy, lists:flatten(Processes)}}. web_specs(Mod, Port) -> WebConfig = [{ip, {0,0,0,0}}, {port, Port}, {docroot, mochiweb_example_deps:local_path(["priv", "www"])}], {Mod, {Mod, start, [WebConfig]}, permanent, 5000, worker, dynamic}.
这里首先调用函数 mochiweb_example_sup:web_specs/2 构建启动mochiweb_example_web工作进程需要的配置,传递模块名:mochiweb_example_web,以及端口号。我们详细看下这个函数。
首先调用 mochiweb_example_deps:local_path/1 函数,并传递参数["priv", "www"],相关的函数代码如下:
%% @spec get_base_dir(Module) -> string() %% @doc Return the application directory for Module. It assumes Module is in %% a standard OTP layout application in the ebin or src directory. get_base_dir(Module) -> {file, Here} = code:is_loaded(Module), D1 = filename:dirname(Here), D2 = filename:dirname(D1), D2. %% @spec get_base_dir() -> string() %% @doc Return the application directory for this application. Equivalent to %% get_base_dir(?MODULE). get_base_dir() -> get_base_dir(?MODULE). %% @spec local_path([string()], Module) -> string() %% @doc Return an application-relative directory from Module's application. local_path(Components, Module) -> filename:join([get_base_dir(Module) | Components]). %% @spec local_path(Components) -> string() %% @doc Return an application-relative directory for this application. %% Equivalent to local_path(Components, ?MODULE). local_path(Components) -> local_path(Components, ?MODULE).
这里不详细讲了,前几篇已经详细介绍了,我们关注下返回值就可以了:"/home/administrator/workplace/mochiweb_example/priv/www"。
再往下就是构建子进程规格,如果你对这部分知识不了解,同样的,请参考《Erlang OTP 设计原理》关于子进程规格的介绍,这里不重复说。
回到mochiweb_example_sup:init/1函数,Strategy则是定义了重启策略,这里也不重复说。
接着,我们直接看工作进程启动时,调用的函数:mochiweb_example_web:start/1,代码如下:
start(Options) -> {DocRoot, Options1} = get_option(docroot, Options), Loop = fun (Req) -> ?MODULE:loop(Req, DocRoot) end, mochiweb_http:start([{name, ?MODULE}, {loop, Loop} | Options1]).
这里首先调用mochiweb_example_web:get_option/2,解析处docroot配置,然后定义提供给mochiweb_http调用的loop函数,最后启动mochiweb_http。
这一篇,就到这里,下一篇,我们来详细分析下这几个函数。
最后,谢谢大家的耐心阅读,大家晚安,好梦。