代码改变世界

mochiweb 源码阅读(五)

2012-07-17 23:42  rhinovirus  阅读(2150)  评论(0编辑  收藏  举报

  今天下班比较早,吃完饭继续跟大家分享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。

  这一篇,就到这里,下一篇,我们来详细分析下这几个函数。

  最后,谢谢大家的耐心阅读,大家晚安,好梦。