代码改变世界

mochiweb 源码阅读(七)

2012-07-22 00:35  rhinovirus  阅读(1763)  评论(0编辑  收藏  举报

  早上去公司待了半天,回来时,雨正大,虽然媳妇早上提前把伞塞我包里,但是小伞还是顶不住激情澎湃的大雨,到家已经是落汤鸡了。不过,还是蛮喜欢雨后的安静。虽然小区楼下蛙声不断,但还是蛮适合看书,Coding的。

  上一篇文章最后,提到了mochiweb_http:start/1函数,这一篇,我们继续往下看:

%% @spec start(Options) -> ServerRet
%%     Options = [option()]
%%     Option = {name, atom()} | {ip, string() | tuple()} | {backlog, integer()}
%%              | {nodelay, boolean()} | {acceptor_pool_size, integer()}
%%              | {ssl, boolean()} | {profile_fun, undefined | (Props) -> ok}
%%              | {link, false}
%% @doc Start a mochiweb server.
%%      profile_fun is used to profile accept timing.
%%      After each accept, if defined, profile_fun is called with a proplist of a subset of the mochiweb_socket_server state and timing information.
%%      The proplist is as follows: [{name, Name}, {port, Port}, {active_sockets, ActiveSockets}, {timing, Timing}].
%% @end
start(Options) ->
    mochiweb_socket_server:start(parse_options(Options)).

  上一篇,我们提到传递进来的参数的值为:

  < Options = [{name,mochiweb_example_web},
             {loop,#Fun<dbg_ieval.9.56107335>},
             {ip,{0,0,0,0}},
             {port,8080}]

  这里我们首先看下 mochiweb_http:parse_options/1 函数:

parse_options(Options) ->
    {loop, HttpLoop} = proplists:lookup(loop, Options),
    Loop = {?MODULE, loop, [HttpLoop]},
    Options1 = [{loop, Loop} | proplists:delete(loop, Options)],
    mochilists:set_defaults(?DEFAULTS, Options1).

  这个函数的作用是解析配置项,函数:proplists:lookup/2,返回List中第一个和Key相关的项,如果存在的话,否则返回none。对于在List中的一个原子A,元组{A, true}与A相关的项 。erlang doc地址:http://www.erlang.org/doc/man/proplists.html#lookup-2,如下图:

  我们可以在shell中测试下这个函数,测试结果如下:

  

  好了,搞清楚这个函数,我们就可以继续往下看了:

parse_options(Options) ->
    {loop, HttpLoop} = proplists:lookup(loop, Options),
    Loop = {?MODULE, loop, [HttpLoop]},
    Options1 = [{loop, Loop} | proplists:delete(loop, Options)],
    mochilists:set_defaults(?DEFAULTS, Options1).

  剩下就没啥难度了,大家一样就能看明白了,第三行从Options中删除旧的loop项,增加一个新的loop项。我们看下最后一行,这里调用 mochilists:set_defaults/2 函数,其中?DEFAULTS定义如下:

-define(DEFAULTS, [{name, ?MODULE},
                   {port, 8888}]).

  mochilists:set_defaults/2 函数代码如下:

%% @spec set_defaults([{Key::term(), Value::term()}], Proplist::list()) -> list()
%%
%% @doc Return new Proplist with {Key, Value} set if not is_defined(Key, Proplist).
set_defaults(DefaultProps, Proplist) ->
    lists:foldl(fun set_default/2, Proplist, DefaultProps).

  这里对DefaultProps的每一项调用mochilists:set_default/2 函数,Proplist作为该函数的第二个参数。lists:foldl/3这个函数,我们在这里系列的第二篇文章讲到过,大家可以翻看mochiweb 源码阅读(二)回忆下。mochilists:set_default/2 函数代码如下:

%% @spec set_default({Key::term(), Value::term()}, Proplist::list()) -> list()
%%
%% @doc Return new Proplist with {Key, Value} set if not is_defined(Key, Proplist).
set_default({Key, Value}, Proplist) ->
    case is_defined(Key, Proplist) of
        true ->
            Proplist;
        false ->
            [{Key, Value} | Proplist]
    end.

  这个函数,首先调用 mochilists:is_defined/2 函数,代码如下:

%% @spec is_defined(Key::term(), Proplist::list()) -> bool()
%%
%% @doc Returns true if Propist contains at least one entry associated
%%      with Key, otherwise false is returned.
is_defined(Key, Proplist) ->
    lists:keyfind(Key, 1, Proplist) =/= false.

  这个函数就一行代码,如果Prolist中的元素的第一个项为Key,则返回该元素,否则返回false,示例如下:

  

  注意这里对L1列表调用的结果,为false。

  我们整理下mochilists:set_defaults/2 函数调用的过程,如下:

-------------------------------------------------------
set_defaults(DefaultProps, Proplist)
-------------------------------------------------------
< DefaultProps = [{name,mochiweb_http},{port,8888}]
< Proplist = [{loop,{mochiweb_http,loop,
                                   [#Fun<mochiweb_example_web.0.8815963>]}},
              {name,mochiweb_example_web},
              {ip,{0,0,0,0}},
              {port,8080}]
-------------------------------------------------------
set_default({Key, Value}, Proplist)
-------------------------------------------------------
< Key = name
< Value = mochiweb_http
< Proplist = [{loop,{mochiweb_http,loop,
                                   [#Fun<mochiweb_example_web.0.8815963>]}},
              {name,mochiweb_example_web},
              {ip,{0,0,0,0}},
              {port,8080}]
-------------------------------------------------------
set_default({Key, Value}, Proplist)
-------------------------------------------------------
< Key = port
< Value = 8888
< Proplist = [{loop,{mochiweb_http,loop,
                                   [#Fun<mochiweb_example_web.0.8815963>]}},
              {name,mochiweb_example_web},
              {ip,{0,0,0,0}},
              {port,8080}]

  通过分析代码的执行过程,我们能够知道,这三个函数的作用,就是检查用户传递进来的配置列表Proplist中,是否存在默认配置列表DefaultProps中的项,如果不存在,则使用默认配置。

  好了,理解完这个函数,mochiweb_http:parse_options/1 函数也就讲完了,我们继续回到调用这个函数的地方,如下:

start(Options) ->
    mochiweb_socket_server:start(parse_options(Options)).

   函数 mochiweb_socket_server:start/1 代码如下:

start(Options) ->
    case lists:keytake(link, 1, Options) of
        {value, {_Key, false}, Options1} ->
            start_server(start, parse_options(Options1));
        _ ->
            %% TODO: https://github.com/mochi/mochiweb/issues/58
            %%   [X] Phase 1: Add new APIs (Sep 2011)
            %%   [_] Phase 2: Add deprecation warning
            %%   [_] Phase 3: Change default to {link, false} and ignore link
            %%   [_] Phase 4: Add deprecation warning for {link, _} option
            %%   [_] Phase 5: Remove support for {link, _} option
            start_link(Options)
    end.

  好了,这一篇就到这里。下一篇我们继续从这个函数往下看。

  也不知道明天天气咋样,报了那么久的驾照培训,明天是第一天上车,希望明天一切顺利,大家好梦。