代码改变世界

mochiweb 源码阅读(十一)

2012-07-29 02:06  rhinovirus  阅读(1647)  评论(0编辑  收藏  举报

  大家好,今天周六,继续接着上一篇,跟大家分享mochiweb源码。上一篇,最后我们看到了mochiweb_socket_server:listen/3函数:

listen(Port, Opts, State=#mochiweb_socket_server{ssl=Ssl, ssl_opts=SslOpts}) ->
    case mochiweb_socket:listen(Ssl, Port, Opts, SslOpts) of
        {ok, Listen} ->
            {ok, ListenPort} = mochiweb_socket:port(Listen),
            {ok, new_acceptor_pool(
                   Listen,
                   State#mochiweb_socket_server{listen=Listen,
                                                port=ListenPort})};
        {error, Reason} ->
            {stop, Reason}
    end.

  我们看下最后一行调用mochiweb_socket_server:new_acceptor_pool/2函数,并且返回{ok, 该函数的返回结果},该函数完整代码如下:

new_acceptor_pool(Listen,
                  State=#mochiweb_socket_server{acceptor_pool=Pool,
                                                acceptor_pool_size=Size,
                                                loop=Loop}) ->
    F = fun (_, S) ->
                Pid = mochiweb_acceptor:start_link(self(), Listen, Loop),
                sets:add_element(Pid, S)
        end,
    Pool1 = lists:foldl(F, Pool, lists:seq(1, Size)),
    State#mochiweb_socket_server{acceptor_pool=Pool1}.

  这个函数,首先定义匿名函数F,这个匿名函数,接受2个参数,其中第一个参数丢弃了,另一个参数为S;接着我们看下第一行,调用mochiweb_acceptor:start_link/3函数,并返回进程标识Pid,接着调用系统函数 sets:add_element/2,我们先看下这个系统函数,erlang doc 地址:http://www.erlang.org/doc/man/sets.html#add_element-2,如下图:

  返回一个新的set由Set1插入Element形成。

  好了,弄清楚匿名函数的逻辑,这里我们先不关注mochiweb_acceptor:start_link/3函数具体内部逻辑,现在只要知道它会返回一个进程标识符就可以了。

  我们继续往下看:

Pool1 = lists:foldl(F, Pool, lists:seq(1, Size)),

  这里首先调用lists:seq/2函数,产生1到Size的列表。这里:acceptor_pool_size=Size=16,取的是mochiweb_socket_server记录acceptor_pool_size字段的默认值。然后依次对这个列表调用匿名函数F,并传递列表中的元素值,以及Pool变量,这里:acceptor_pool=Pool=sets:new(),这里也是取的mochiweb_socket_server记录acceptor_pool字段的默认值。注意:之前匿名函数F的第一个参数为丢弃的值:_,表示这里并不需要这个元素的值,只是循环调用这个匿名函数Size次,并且每次都是在之前的set基础上增加新的Element,并返回。

  最后把State#mochiweb_socket_server字段acceptor_pool的值修改为返回的最新的set类型的变量Pool1。

  好了,到目前为止,我们已经弄明白了new_acceptor_pool函数,回过头来,我们看下之前没有详细看的mochiweb_acceptor:start_link/3函数:  

start_link(Server, Listen, Loop) ->
    proc_lib:spawn_link(?MODULE, init, [Server, Listen, Loop]).

  这个函数很简单,就一行逻辑,调用系统函数:proc_lib:spawn_link/3,erlang doc 地址:http://www.erlang.org/doc/man/proc_lib.html#spawn_link-3,如下图:

  这个函数的作用和erlang模块下的spawn_link功能类似,都是初始化一个进程来执行指定模块下的指定函数。关于用proc_lib创建的进程有什么与众不同,这里不打算多说,大家可以参考坚强2002同学的,这篇文章:[Erlang 0017]Erlang/OTP基础模块 proc_lib

   接下来我们就可以看下mochiweb_acceptor:init/3函数,完整代码如下:

init(Server, Listen, Loop) ->
    T1 = now(),
    case catch mochiweb_socket:accept(Listen) of
        {ok, Socket} ->
            gen_server:cast(Server, {accepted, self(), timer:now_diff(now(), T1)}),
            call_loop(Loop, Socket);
        {error, closed} ->
            exit(normal);
        {error, timeout} ->
            init(Server, Listen, Loop);
        {error, esslaccept} ->
            exit(normal);
        Other ->
            error_logger:error_report(
              [{application, mochiweb},
               "Accept failed error",
               lists:flatten(io_lib:format("~p", [Other]))]),
            exit({error, accept_failed})
    end.

  接下来,我们来详细看下这个函数的具体逻辑:

T1 = now()

  这一行,调用系统函数erlang:now/0,erlang doc 地址:http://www.erlang.org/doc/man/erlang.html#now-0,如下图:

  测试如下:

  

  好了,这一篇就到这里,下一篇,我们将从mochiweb_socket:accept/1继续往下看。

  最后,谢谢大家的支持,晚安。