nginx过滤器模块

来源:互联网 发布:苹果mac怎么关闭程序 编辑:程序博客网 时间:2024/06/02 06:40

        nginx服务器给客户端发送响应时,包括http响应头部、http响应包体内容。可以调用http框架提供的两个函数ngx_http_send_header,ngx_http_output_filter,分别用于给客户端发送http响应头部、http响应包体。这两个函数会调用各个过滤器模块,对将要发送给客户端的响应头部、响应包体进行过滤处理。例如: 是否需要添加http响应头部字段、是否需要断点续传、是否只发送一个区间块的数据、响应包体是否需要压缩等。

一、过滤器模块链表创建

        nginx服务器有一个全局的变量ngx_http_top_header_filter,这是一个函数指针变量。 也是http响应头部过滤链表的头部。对于每一个过滤模块,都会有一个静态的变量ngx_http_next_header_filter,同样这也是一个函数指针变量,指向过滤链表的下一个节点。例如:在由6个过滤器模块组成的http响应头部过滤链表内存布局如下:


        从这张图可以看出,链表采用的是头插法。也就是说第一个过滤器模块变成了链表的尾节点,而最后一个过滤器模块变成了链表的首节点。同样的http响应包体组成的过滤链表内存布局和上图是类似的,都采用的是头插法。只不过http响应过滤包体链表头指针为一个全局的函数指针ngx_http_top_body_filter,每一个http过滤模块的静态变量ngx_http_next_body_filter则组成响应包体链表的next指针。

        下面从代码角度分析下这个过滤链表是如何创建的。以http响应头部过滤链表的创建为例。

        ngx_http_header_filter_module是第一个http响应头部过滤模块,在这个模块的ngx_http_header_filter_init函数中,将ngx_http_top_header_filter函数指针指向ngx_http_header_filter, 此时这个为链表的第一个节点。

static ngx_int_t ngx_http_header_filter_init(ngx_conf_t *cf){    ngx_http_top_header_filter = ngx_http_header_filter;    return NGX_OK;}
        ngx_http_chunked_filter_module是第二个http响应头部过滤模块,在这个模块的ngx_http_chunked_filter_init函数中将ngx_http_next_header_filter指向ngx_http_top_header_filter,也就是第一个http响应过滤头部模块,而ngx_http_top_header_filter指向本模块的回调ngx_http_chunked_header_filte,使用头部插入法构成一个链表。

static ngx_int_t ngx_http_chunked_filter_init(ngx_conf_t *cf){    ngx_http_next_header_filter = ngx_http_top_header_filter;   //next指针指向之前的模块    ngx_http_top_header_filter = ngx_http_chunked_header_filter;//top指针指向当前这个模块的回调    return NGX_OK;}
         其它过滤器模块插入到链表的过程是类似的,这里就不再对每一个过滤模块如何插入到链表进行详细分析了, 没有这个必要。
二、常见http过滤模块的功能

        过滤模块是比较简单的模块,对于每一个过滤模块,代码量也不过几百行而已。仔细分析很容易看懂,这里就不对这些过滤模块进行详细分析了。但需要总结下几个常见过滤模块是做什么的。

三、发送响应头部

        ngx_http_send_header函数用于发送http响应头部,这个是http框架提供的接口,可以被http模块调用。看下这个函数的实现。

ngx_int_t ngx_http_send_header(ngx_http_request_t *r){    if (r->err_status) {        r->headers_out.status = r->err_status;        r->headers_out.status_line.len = 0;    }    return ngx_http_top_header_filter(r);}
        这个函数会从http响应头部过滤器链表头部开始遍历,使用各个过滤器模块对响应头部进行处理,最终把处理后的响应头部发送给客户端。而最后一个http响应头部过滤器链表节点的回调为:ngx_http_header_filter,看下这个函数的实现。
//将http响应头部的各个头部信息拼装到buf缓冲区中,然后将这个缓冲区的数据发送给客户端static ngx_int_t ngx_http_header_filter(ngx_http_request_t *r){/*********以下是为了统计响应头部的总大小         **********//*********目的是为了开辟一个应答包头大小的缓冲区 **********/    len = sizeof("HTTP/1.x ") - 1 + sizeof(CRLF) - 1 + sizeof(CRLF) - 1;//统计状态码字符串的长度//status_line存放的是状态码的内容,例如:"302 Moved Temporarily"    if (r->headers_out.status_line.len){        len += r->headers_out.status_line.len;        status_line = &r->headers_out.status_line;    }     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);//服务器的版本信息长度    if (r->headers_out.server == NULL) {        len += clcf->server_tokens ? sizeof(ngx_http_server_full_string) - 1:                                     sizeof(ngx_http_server_string) - 1;    }//开辟一个buf结构,buf大小为应答包头的大小    b = ngx_create_temp_buf(r->pool, len);    if (b == NULL) {        return NGX_ERROR;    }/********以下操作将http应答包头的各个字段拷贝到开辟的缓冲区***********///拷贝HTTP1.1 200 OK状态行    /* "HTTP/1.x " */    b->last = ngx_cpymem(b->last, "HTTP/1.1 ", sizeof("HTTP/1.x ") - 1);    if (status_line) {        b->last = ngx_copy(b->last, status_line->data, status_line->len);    }     *b->last++ = CR; *b->last++ = LF;//拷贝应答包体长度字段    if (r->headers_out.content_length == NULL        && r->headers_out.content_length_n >= 0)    {        b->last = ngx_sprintf(b->last, "Content-Length: %O" CRLF,r->headers_out.content_length_n);    }//构造发送缓冲区    out.buf = b;    out.next = NULL;//发送http响应头部信息给客户端    return ngx_http_write_filter(r, &out);}
四、发送响应包体

        ngx_http_output_filter函数用于向客户端发送http响应包体数据。这个是http框架提供的接口,可以被http模块调用。看下这个函数的实现:

ngx_int_t ngx_http_output_filter(ngx_http_request_t *r, ngx_chain_t *in){    ngx_int_t          rc;    ngx_connection_t  *c;//发送响应包体    rc = ngx_http_top_body_filter(r, in);    return rc;}
        这个函数会从http响应包体过滤器链表头部开始遍历,使用各个过滤器模块对响应包体进行处理,最终把处理后的响应包体发送给客户端。而最后一个http响应包体过滤器链表节点的回调为:ngx_http_write_filter,看下这个函数的实现。

        ngx_http_write_filter这个函数会将响应包体发送给客户端,如果一次没法发送完成,则会保存未发送完成的数据到链表中,同时删除已经发送完成的链表数据。

ngx_int_t ngx_http_write_filter(ngx_http_request_t *r, ngx_chain_t *in){//计算out缓冲区占用字数数    for (cl = r->out; cl; cl = cl->next) {        ll = &cl->next;        size += ngx_buf_size(cl->buf);    }    /* add the new chain to the existent one *///将本次待发送的缓冲区加到out尾部,并计算出out总长度    for (ln = in; ln; ln = ln->next) {        cl = ngx_alloc_chain_link(r->pool);        cl->buf = ln->buf;        *ll = cl;        ll = &cl->next;//计算这个链表节点缓冲区数据大小        size += ngx_buf_size(cl->buf);    }    *ll = NULL;//待发送的响应包体没有达到阈值,则暂时不发送    if (!last && !flush && in && size < (off_t) clcf->postpone_output) {        return NGX_OK;    }//需要延迟发送响应,用于限速功能,本次不发送的会,对客户端来讲,自然速度降低了    if (c->write->delayed) {        c->buffered |= NGX_HTTP_WRITE_BUFFERED;        return NGX_AGAIN;    }//判断是否需要限速    if (r->limit_rate) {        limit = r->limit_rate * (ngx_time() - r->start_sec + 1)                - (c->sent - clcf->limit_rate_after);//limit小于等于0,表示超发的字节数,需要限速        if (limit <= 0) {            c->write->delayed = 1;//需要延迟发送响应            ngx_add_timer(c->write, (ngx_msec_t) (- limit * 1000 / r->limit_rate + 1));            c->buffered |= NGX_HTTP_WRITE_BUFFERED;            return NGX_AGAIN;        }//计算本次最大可以发送多少数据给客户端        if (clcf->sendfile_max_chunk            && (off_t) clcf->sendfile_max_chunk < limit)        {            limit = clcf->sendfile_max_chunk;        }    }else {        limit = clcf->sendfile_max_chunk;    }//调用ngx_linux_sendfile_chain发送数据,返回值是未发送完的数据    chain = c->send_chain(c, r->out, limit);//ngx_writev_chain//清空已经发送的缓冲    for (cl = r->out; cl && cl != chain; /* void */){        ln = cl;        cl = cl->next;        ngx_free_chain(r->pool, ln);    }//保存未发送完成的数据,待下次事件触发后在发送给客户端    r->out = chain;    return NGX_OK;}
        到此过滤器模块已经分析完成了。过滤器模块还是比较简单的,代码量也不太,读者可以好好分析上图中的这6个过滤器模块的实现方式。下一篇文章将分析nginx是如何接收http请求包体的,如果不需要接收请求包体,那nginx又是如何丢弃http请求包体的。


2 0
原创粉丝点击