Nginx源代码分析之spdy(十五)

原创 2016年08月30日 18:02:32

nginx作为服务端,在建立socket并listen之后,会设置accept返回的异步回调,代码在ngx_http_add_listening中:

 ls->handler = ngx_http_init_connection;


在ngx_event_accept.c的ngx_event_accept函数中会执行这个回调,再来看看ngx_http_init_connection,再前面一节已经介绍过,这里会注册读取数据的异步回调ngx_http_wait_request_handler,但如果使用的是spdy,则会用spdy的函数来覆盖这个函数指针,代码如下:

    rev = c->read;
    rev->handler = ngx_http_wait_request_handler;
    c->write->handler = ngx_http_empty_handler;


#if (NGX_HTTP_SPDY)
    if (hc->addr_conf->spdy) {
        rev->handler = ngx_http_spdy_init;
    }
#endif

ngx_http_spdy_init会初始化与spdy相关的所有配置,

ngx_http_spdy_init的最后会设置ngx_http_spdy_read_handler作为读回调,等待连接数据,如果已经有数据,则立即执行。而在ngx_http_spdy_read_handler中,首先调用异步recv请求,如果已经收到完整数据,则会执行sc->handler,这就是之前注册的ngx_http_spdy_state_head。

最后如果sc->processing 大于0,表示当前connection之上仍然有spdy流,此时直接返回,如果小于等于0,则调用ngx_http_spdy_handle_connection,这个函数用于判断是否结束当前连接,由于spdy属于长连接,在当前连接没发生错误的情况下,会设置读回调ngx_http_spdy_keepalive_handler,并保留当前的连接。


现在回头说说spdy真正的入口,即ngx_http_spdy_state_head,他会解析来自客户端的spdy请求的头部,然后根据frame_type字段来识别请求的类型,spdy的请求分成2大类,控制帧和数据帧。具体的在代码中可见:

    if (ngx_spdy_ctl_frame_check(head)) {
        type = ngx_spdy_ctl_frame_type(head);


        switch (type) {


        case NGX_SPDY_SYN_STREAM:
            return ngx_http_spdy_state_syn_stream(sc, pos, end);


        case NGX_SPDY_SYN_REPLY:
            ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,
                          "client sent unexpected SYN_REPLY frame");
            return ngx_http_spdy_state_protocol_error(sc);


        case NGX_SPDY_RST_STREAM:
            return ngx_http_spdy_state_rst_stream(sc, pos, end);


        case NGX_SPDY_SETTINGS:
            return ngx_http_spdy_state_settings(sc, pos, end);


        case NGX_SPDY_PING:
            return ngx_http_spdy_state_ping(sc, pos, end);


        case NGX_SPDY_GOAWAY:
            return ngx_http_spdy_state_skip(sc, pos, end); /* TODO */


        case NGX_SPDY_HEADERS:
            ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,
                          "client sent unexpected HEADERS frame");
            return ngx_http_spdy_state_protocol_error(sc);


        case NGX_SPDY_WINDOW_UPDATE:
            return ngx_http_spdy_state_window_update(sc, pos, end);


        default:
            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,
                           "spdy control frame with unknown type %ui", type);
            return ngx_http_spdy_state_skip(sc, pos, end);
        }
    }


    if (ngx_spdy_data_frame_check(head)) {
        sc->stream = ngx_http_spdy_get_stream_by_id(sc, head);
        return ngx_http_spdy_state_data(sc, pos, end);
    }


    ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,
                  "client sent invalid frame");


    return ngx_http_spdy_state_protocol_error(sc);
}


      先来看看spdy的流的创建,当对方发送的是SYN_STREAM帧的话,,调用ngx_http_spdy_state_syn_stream来创建一个新的流,,这个函数包含大量细节,不再细说,真正创生产spdy流的是ngx_http_spdy_create_stream,它返回一个ngx_http_spdy_stream_t的结构,代表了一个流。一些关键代码在函数末尾:

    r->spdy_stream = stream;


    stream->id = id;
    stream->request = r;
    stream->connection = sc;


    stream->send_window = sc->init_window;
    stream->recv_window = NGX_SPDY_STREAM_WINDOW;


    stream->priority = priority;


    sscf = ngx_http_get_module_srv_conf(r, ngx_http_spdy_module);


    index = ngx_http_spdy_stream_index(sscf, id);


    stream->index = sc->streams_index[index];
    sc->streams_index[index] = stream;


    sc->processing++;

作用主要是把stream保存到ngx_http_spdy_stream_t的成员stgreams_index中,这就是说:1个tcp连接可以同时发起多个并行的spdy流,同时设置当前流的优先级,这个优先级是从客户端的请求包中解析出来的,还同时把当前流的指针赋值给http_request的spdy_stream,最后把processing加一。

       这个函数之后,再调用ngx_http_spdy_state_headers,spdy的头部是加密的,spdy把HTTP Header中常见的单词定义了字典,通过Zlib对Header进行压缩,压缩以帧为单位,每次压缩后做SYNC_FLUSH,但是不reset,所以压缩状态能够保存,从第二个包含Header的帧开始,压缩率大幅提升,因为HTTP请求和响应的Header重复内容很多。对于Body,spdy协议本身并不压缩,不过它规定从它这经过的HTTP请求必须强制开启GZip压缩。

压缩调用的是

z = inflate(&sc->zstream_in, Z_NO_FLUSH);


然后再处理解密的所有头部字段,大致原理是循环判断sc->entries,循环中先调用ngx_http_spdy_parse_header解析出一个name/value块,如果成功,接着调用ngx_http_spdy_handle_request_header,根据这些name/value块找到已经定义的处理函数,这些name/value块类似http的头部字段,每个头部会有一个处理函数:



        for (i = 0; i < NGX_SPDY_REQUEST_HEADERS; i++) {
            sh = &ngx_http_spdy_request_headers[i];


            if (sh->hash != r->header_hash
                || sh->len != r->header_name_end - r->header_name_start
                || ngx_strncmp(sh->header, r->header_name_start, sh->len) != 0)
            {
                continue;
            }


            return sh->handler(r);
        }

每个字段的处理函数是固定在代码中的

static ngx_http_spdy_request_header_t ngx_http_spdy_request_headers[] = {
    { 0, 6, "method", ngx_http_spdy_parse_method },
    { 0, 6, "scheme", ngx_http_spdy_parse_scheme },
    { 0, 4, "host", ngx_http_spdy_parse_host },
    { 0, 4, "path", ngx_http_spdy_parse_path },
    { 0, 7, "version", ngx_http_spdy_parse_version },
};


       这些块经过处理后,就形成了一个普通的ngx_http_request_t的请求,这时在调用ngx_http_spdy_run_request->ngx_http_process_request,这样就进入了普通http的处理流程。因此,spdy实际上是在tcp和http协议间额外加入的一个协议/协议层,主要功能是压缩头部和在同一个页面中发起多个并发的http请求,加快大页面的记载速度。

       另外,一个大页面如何分解成多个并行的spdy请求,这些细节需要看支持spdy的客户端代码,在chromium中可以找到这样的代码,以后有空再分析吧。


为了更清晰的理解spdy源码,可以看看google的spdy协议:

http://dev.chromium.org/spdy/spdy-protocol/spdy-protocol-draft3-1#TOC-1.2-Definitions

另外有个中文的精简版本:

http://network.51cto.com/art/201509/492693.htm



版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

Nginx源代码分析之accept(十六)

accpet 的初始化和调用可以从work进程的启动开始讲起 在unix系平台上,work进程的主函数是ngx_worker_process_cycle ngx_worker_process_cy...

Nginx源代码分析之群惊问题(十七)

接上一节,在accept初始化的时候有一个ngx_use_accept_mutex变量,这是用来解决当多个进程在一个套接口上同时调用accept引起的群惊问题的。

Nginx源代码分析之accept细节(十九)

现在单独说说accept中与一些具体I/O模型相关的细节。         其实前面已经提过这个问题,这一系列I/O模型中最大差别是iocp,我们前面也说了,linux的几个模型,不管是select,...

Nginx源代码分析之HTTP请求响应基本流程(十四)

HTTP的处理请求流程我们从ngx_http_init_connection开始论述 里面注册了一个处理函数 rev->handler = ngx_http_wait_request_handler;...

Nginx源代码分析之HTTP2.0(二十)

http/2在谷歌的推动下几乎就是spdy的升级,nginx目前已经加入了http2的代码,结构跟spdy有一定相似度。 其入口函数是ngx_http_v2_init

Source Insight 3.x serial 序列号

Source Insight 3.x 序列号

Nginx源代码分析之锁的实现(十八)

转载一篇nginx锁的细节的文章 http://wang.peng.1123.blog.163.com/blog/static/129821112201381311441180/  Nginx中的锁...

nginx lua 安装spdy

关于spdy摘自 http://zh.wikipedia.org/wiki/SPDYSPDYSPDY是Google开发的基于传输控制协议(TCP)的应用层协议 。Google最早是在Chromium中...

nginx的spdy协议

SPDY 是 Google 开发的基于传输控制协议 (TCP) 的应用层协议 ,开发组正在推动 SPDY 成为正式标准(现为互联网草案)。SPDY 协议旨在通过压缩、多路复用和优先级来缩短网页的加...

Nginx下让SSL支持SPDY协议

SPDY介绍 SPDY 是 Google 开发的基于传输控制协议(TCP)的应用层协议,开发组正在推动 SPDY 成为正式标准(现为互联网草案)。SPDY 协议类似于 HTTP,但旨在缩短网页的...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)