代码改变世界

mochiweb 源码阅读(二)

2012-07-14 00:09  rhinovirus  阅读(4365)  评论(7编辑  收藏  举报

  大家好,几天没跟新了,在上一篇,我们简单介绍了mochiweb这个项目,以及下载,编译,创建mochiweb的源码以及示例。今天继续跟大家来分析mochiweb这个项目,跟之前分析cowboy的方法一样,我们找到切入口点,来一一分析每个函数的功能,作用,下面看下上一篇创建的示例都有哪些文件,如下图:

  

  这里除了keepalive.erl 是我从官方example考过来以外,其他为上一篇创建的例子的源码。

  我不一一介绍这几个文件了,参考我一直给大家推荐的Erlang OTP 设计原理。好好看几遍,你就对Erlang应用程序有一定的了解了。

  我们从运行程序的 mochiweb_example:start/0 开始看吧,代码如下:

%% @spec start() -> ok
%% @doc Start the mochiweb_example server.
start() ->
    mochiweb_example_deps:ensure(),
    ensure_started(crypto),
    application:start(mochiweb_example).

  这个函数三行代码,第一行先跳过,一会我们重点看下。

  第二行启动crypto,mochiweb_example:ensure_started/1,代码如下:

ensure_started(App) ->
    case application:start(App) of
        ok ->
            ok;
        {error, {already_started, App}} ->
            ok
    end.

  很简单,判断下该应用程序是否启动,如果没启动就启动,如果已经启动了,就提示 {error, {already_started, App}} 错误。

  第三行,启动mochiweb_example,很简单,其实这里也可以修改成这样:ensure_started(mochiweb_example),因为上面那个函数本来也就是启动应用程序用的。不知道作者为什么生成代码,不用上面的函数,不过关系不大。

  现在可以重点看下:mochiweb_example_deps:ensure/0 这个函数了,代码如下:

%% @spec ensure(Module) -> ok
%% @doc Ensure that all ebin and include paths for dependencies
%%      of the application for Module are on the code path.
ensure(Module) ->
    code:add_paths(new_siblings(Module)),
    code:clash(),
    ok.

%% @spec ensure() -> ok
%% @doc Ensure that the ebin and include paths for dependencies of
%%      this application are on the code path. Equivalent to
%%      ensure(?Module).
ensure() ->
    ensure(?MODULE).

  从上面代码可以知道 mochiweb_example_deps:ensure/0 调用 mochiweb_example_deps:ensure/1 函数,并传递参数 ?MODULE,而mochiweb_example_deps:ensure/1 函数,就三行代码也很简单,我们先看下 mochiweb_example_deps:new_siblings/1 函数:

%% @spec new_siblings(Module) -> [Dir]
%% @doc Find new siblings paths relative to Module that aren't already on the
%%      code path.
new_siblings(Module) ->
    Existing = deps_on_path(),
    SiblingEbin = filelib:wildcard(local_path(["deps", "*", "ebin"], Module)),
    Siblings = [filename:dirname(X) || X <- SiblingEbin,
                           ordsets:is_element(
                             filename:basename(filename:dirname(X)),
                             Existing) =:= false],
    lists:filter(fun filelib:is_dir/1,
                 lists:append([[filename:join([X, "ebin"]),
                                filename:join([X, "include"])] ||
                                  X <- Siblings])).

  从这个函数的注释,我们能大概知道,查找相对于Module模块不在代码路径下的新的路径。看下具体逻辑,首先是 mochiweb_example_deps:deps_on_path/0 函数,代码如下:

%% @spec deps_on_path() -> [ProjNameAndVers]
%% @doc List of project dependencies on the path.
deps_on_path() ->
    F = fun (X, Acc) ->
                ProjDir = filename:dirname(X),
                case {filename:basename(X),
                      filename:basename(filename:dirname(ProjDir))} of
                    {"ebin", "deps"} ->
                        [filename:basename(ProjDir) | Acc];
                    _ ->
                        Acc
                end
        end,
    ordsets:from_list(lists:foldl(F, [], code:get_path())).

  这个函数,有不少的系统函数,我们先看下这几个函数:

  函数:filename:dirname/1,返回当前文件名所在的目录,大家可以查下erlang doc,地址:http://www.erlang.org/doc/man/filename.html#dirname-1 我截了个图,方便大家看吧,如下图:

  函数filename:basename/1,大概意思是:如果它不包含任何目录分隔符,返回最后一部分的文件名或文件名本身。同样查下erlang doc,地址:http://www.erlang.org/doc/man/filename.html#basename-1,如下图:

  函数:code:get_path/0,返回当前代码路径。 erlang doc 地址:http://www.erlang.org/doc/man/code.html#get_path-0,如下图:

  

  我在shell上尝试打印出完整的列表(使用rp命令),一共是58项,如下图:

  

  

  函数:lists:foldl/3,对List中的每一项执行函数Fun,Acc0作为该函数第二个参数。 erlang doc 地址:http://www.erlang.org/doc/man/lists.html#foldl-3,如下图:

  我们做个练习如下:

  

  函数: ordsets:from_list/1,对List排序,erlang doc 地址:http://www.erlang.org/doc/man/ordsets.html#from_list-1,如下图:

   示例如下:

  

  理解完这几个系统函数,我们来整理下 mochiweb_example_deps:deps_on_path/0 这个函数的作用。首先是定义了匿名函数F,接受2个参数,X为调用 code:get_path/0 返回的列表的每一项项,Acc用来保存对每一项处理的结果。而处理每一项的逻辑也很简单,这里我们用Debugger跟踪下其中一项的处理过程,如下图:

  图一:

  

  图二:

  

  图三:

  

  根据上面三个图,我们就能更加清楚这个函数的作用了。

  好了,这一篇就到这里,这些系统函数需要多动手练习还会熟悉。希望大家也多多动手,下一篇,我们继续从mochiweb_example_deps:new_siblings/1 函数往下看。

  谢谢大家的收看。