代码改变世界

Cowboy 源码分析(二十七)

2012-07-08 12:42  rhinovirus  阅读(1491)  评论(0编辑  收藏  举报

  大家好,从2012-05-15号开始的第一篇Cowboy源码分析,到上一篇2012-07-04的第二十六篇为止,我们已经完整的看完了,Cowboy的启动,处理来自浏览器的请求,响应等整个过程。这这一篇,我们来做个总结。毕竟这是我第一个接触的Erlang开源项目,今天回过头来,从整体上去看这个项目。

  首先是cowboy应用的启动,我们从observer上可以看到,当该应用程序启动时,启动进程情况,如下图:

  

  我们右键可以查看该进程的相关信息,这点observer做的还是挺方便的,如下图:

  

  下图为打开的进程信息窗口:

  

  从上面可以很容易的获取一些和该进程相关的信息,比如进程名称,消息队列长度,和该进程连接的进程标识符,以及内存占用的相关数据等等。

  我们看到当cowboy程序启动时,会启动cowboy_clock这个模块,关于这个模块的作用我不多说,相信看过之前文章的都能知道。

  接下来我们看看cowboy_examples启动,如下图:

  

  下面这张,依然是 cowboy应用的新的情况,我们可以看到附加了许多子进程:

  

  当然,由于启动的进程较多,这里并不能完整的看到,我只截取了部分,大家可以自己运行observer看看。

  这里有个注意的地方,我刚开始比较没注意,看下 cowboy_examples_app:start/2 代码:

start(_Type, _Args) ->
    Dispatch = [
        {'_', [
            {[<<"websocket">>], websocket_handler, []},
            {[<<"eventsource">>], eventsource_handler, []},
            {[<<"eventsource">>, <<"live">>], eventsource_emitter, []},
            {'_', default_handler, []}
        ]}
    ],
    cowboy:start_listener(my_http_listener, 100,
        cowboy_tcp_transport, [{port, 8080}],
        cowboy_http_protocol, [{dispatch, Dispatch}]
    ),
%%    cowboy:start_listener(my_https_listener, 100,
%%        cowboy_ssl_transport, [
%%            {port, 8443}, {certfile, "priv/ssl/cert.pem"},
%%            {keyfile, "priv/ssl/key.pem"}, {password, "cowboy"}],
%%        cowboy_http_protocol, [{dispatch, Dispatch}]
%%    ),
    cowboy_examples_sup:start_link().

  这其实是2个例子的,所以我当时看的时候发现两棵完全一样的树,没注意到是两个例子,这里为了方便分析,我注释掉下面这个例子。

  编译,重新运行,这回就明朗多了,呵呵。如下图:

  

  下面我把几处关键代码贴出来,大家就能理解这里所有进程的是如何启动:

  代码块一:

-spec start_listener(any(), non_neg_integer(), module(), any(), module(), any())
    -> {ok, pid()}.
start_listener(Ref, NbAcceptors, Transport, TransOpts, Protocol, ProtoOpts)
        when is_integer(NbAcceptors) andalso is_atom(Transport)
        andalso is_atom(Protocol) ->
    supervisor:start_child(cowboy_sup, child_spec(Ref, NbAcceptors,
        Transport, TransOpts, Protocol, ProtoOpts)).

  代码块二:

-spec child_spec(any(), non_neg_integer(), module(), any(), module(), any())
    -> supervisor:child_spec().
child_spec(Ref, NbAcceptors, Transport, TransOpts, Protocol, ProtoOpts)
        when is_integer(NbAcceptors) andalso is_atom(Transport)
        andalso is_atom(Protocol) ->
    {{cowboy_listener_sup, Ref}, {cowboy_listener_sup, start_link, [
        NbAcceptors, Transport, TransOpts, Protocol, ProtoOpts
    ]}, permanent, 5000, supervisor, [cowboy_listener_sup]}.

  代码块三:

-spec start_link(non_neg_integer(), module(), any(), module(), any())
    -> {ok, pid()}.
start_link(NbAcceptors, Transport, TransOpts, Protocol, ProtoOpts) ->
    MaxConns = proplists:get_value(max_connections, TransOpts, 1024),
    {ok, SupPid} = supervisor:start_link(?MODULE, []),
    {ok, ListenerPid} = supervisor:start_child(SupPid,
        {cowboy_listener, {cowboy_listener, start_link, [MaxConns, ProtoOpts]},
         permanent, 5000, worker, [cowboy_listener]}),
    {ok, ReqsPid} = supervisor:start_child(SupPid,
        {cowboy_requests_sup, {cowboy_requests_sup, start_link, []},
         permanent, 5000, supervisor, [cowboy_requests_sup]}),
    {ok, _PoolPid} = supervisor:start_child(SupPid,
        {cowboy_acceptors_sup, {cowboy_acceptors_sup, start_link, [
            NbAcceptors, Transport, TransOpts,
            Protocol, ProtoOpts, ListenerPid, ReqsPid
        ]}, permanent, 5000, supervisor, [cowboy_acceptors_sup]}),
    {ok, SupPid}.

  重点在代码块三,这里的 SupPid 就是添加到 cowboy_sup 的子进程,而工作进程cowboy_listener,以及监控进程cowboy_requests_sup,监控进程cowboy_acceptors_sup都是连接到 SupPid 进程的子进程。

  还有一处添加100个子进程到cowboy_acceptors_sup监控进程的代码如下:

-spec start_link(non_neg_integer(), module(), any(),
    module(), any(), pid(), pid()) -> {ok, pid()}.
start_link(NbAcceptors, Transport, TransOpts,
        Protocol, ProtoOpts, ListenerPid, ReqsPid) ->
    supervisor:start_link(?MODULE, [NbAcceptors, Transport, TransOpts,
        Protocol, ProtoOpts, ListenerPid, ReqsPid]).

%% supervisor.

-spec init([any()]) -> {'ok', {{'one_for_one', 10, 10}, [{
    any(), {atom() | tuple(), atom(), 'undefined' | [any()]},
    'permanent' | 'temporary' | 'transient',
    'brutal_kill' | 'infinity' | non_neg_integer(),
    'supervisor' | 'worker',
    'dynamic' | [atom() | tuple()]}]
}}.
init([NbAcceptors, Transport, TransOpts,
        Protocol, ProtoOpts, ListenerPid, ReqsPid]) ->
    {ok, LSocket} = Transport:listen(TransOpts),
    Procs = [{{acceptor, self(), N}, {cowboy_acceptor, start_link, [
                LSocket, Transport, Protocol, ProtoOpts,
                ListenerPid, ReqsPid
      ]}, permanent, brutal_kill, worker, []}
        || N <- lists:seq(1, NbAcceptors)],
    {ok, {{one_for_one, 10, 10}, Procs}}.

  好了,当cowboy_examples启动时,关于进程启动的这块,我们总结完了,下一篇我们看看处理用户请求的这块吧。

  谢谢大家。