Nginx 源码分析(9)——filter模块

本文详细阐述了nginx中响应内容的输出流程,包括header和body的生成与发送,以及如何通过filter模块进行定制化处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

        phase handler处理中介绍了content handler用于产生响应内容,随便找一个content phase的模块,比如:ngx_http_static_module.c,会发现在content handler中会调用ngx_http_send_header,然后最后调用ngx_http_output_filter。这两个函数就是发送响应头部和响应体的,在nginx中输出内容是通过filter完成的。

        filter模块用于过滤和输出响应内容,nginx将所有的filter组织成只有头结点的单链表(实际上就是栈),这个头结点分别是ngx_http_top_header_filter(过滤头部)和ngx_http_top_body_filter(过滤响应体)。在每次初始化一个filter模块时,都会将当前filter链表的头结点ngx_http_top_header_filter和ngx_http_top_body_filter分别保存为ngx_http_next_header_filter和ngx_http_next_body_filter,同时将本模块的filter函数保存为ngx_http_top_header_filter和ngx_http_top_body_filter,然后在本模块filter函数的最后部分调用ngx_http_next_header_filter和ngx_http_next_body_filter,这样就实现了所有filter模块的链式调用。由于filter模块的添加实际上就是不停的向链表的头push节点,所以后添加的模块比先添加的模块在filter链中靠前。

        先看一下ngx_http_send_header函数:

 

[cpp] view plaincopyprint?
  1. ngx_int_t 
  2. ngx_http_send_header(ngx_http_request_t *r) 
  3.     if (r->err_status) { 
  4.         r->headers_out.status = r->err_status; 
  5.         r->headers_out.status_line.len = 0; 
  6.     } 
  7.  
  8.     return ngx_http_top_header_filter(r); 
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);
}
        实现很简单,就是调用ngx_http_top_header_filter函数,然后这个函数后调用接下来的filter直到filter链遍历完为止。ngx_http_output_filter与之类似,调用ngx_http_top_body_filter。

 

        filter的添加都是在http模块的postconfiguration回调函数中完成的,这部分调用是在ngx_http_block中:

 

[cpp] view plaincopyprint?
  1. for (m = 0; ngx_modules[m]; m++) { 
  2.     if (ngx_modules[m]->type != NGX_HTTP_MODULE) { 
  3.         continue
  4.     } 
  5.  
  6.     module = ngx_modules[m]->ctx; 
  7.  
  8.     if (module->postconfiguration) { 
  9.         if (module->postconfiguration(cf) != NGX_OK) { 
  10.             return NGX_CONF_ERROR; 
  11.         } 
  12.     } 
    for (m = 0; ngx_modules[m]; m++) {
        if (ngx_modules[m]->type != NGX_HTTP_MODULE) {
            continue;
        }

        module = ngx_modules[m]->ctx;

        if (module->postconfiguration) {
            if (module->postconfiguration(cf) != NGX_OK) {
                return NGX_CONF_ERROR;
            }
        }
    }

        回调函数的调用顺序是遍历ngx_modules数组,然后逐一调用postconfiguration函数,那么filter模块在ngx_modules数组中的顺序就决定了其filter函数在filter链中位置。下面是ngx_modules数组的内容:

 

[cpp] view plaincopyprint?
  1. ngx_module_t *ngx_modules[] = { 
  2.     &ngx_core_module, 
  3.     //……  
  4.     &ndk_http_module, 
  5.     &ngx_http_write_filter_module, 
  6.     &ngx_http_header_filter_module, 
  7.     &ngx_http_chunked_filter_module, 
  8.     &ngx_http_range_header_filter_module, 
  9.     &ngx_http_gzip_filter_module, 
  10.     &ngx_http_postpone_filter_module, 
  11.     &ngx_http_ssi_filter_module, 
  12.     &ngx_http_charset_filter_module, 
  13.     &ngx_http_userid_filter_module, 
  14.     &ngx_http_headers_filter_module, 
  15.     &ngx_http_set_misc_module, 
  16.     &ngx_http_echo_module, 
  17.     &ngx_http_copy_filter_module, 
  18.     &ngx_http_range_body_filter_module, 
  19.     &ngx_http_not_modified_filter_module, 
  20.     NULL 
  21. }; 
ngx_module_t *ngx_modules[] = {
    &ngx_core_module,
    //……
    &ndk_http_module,
    &ngx_http_write_filter_module,
    &ngx_http_header_filter_module,
    &ngx_http_chunked_filter_module,
    &ngx_http_range_header_filter_module,
    &ngx_http_gzip_filter_module,
    &ngx_http_postpone_filter_module,
    &ngx_http_ssi_filter_module,
    &ngx_http_charset_filter_module,
    &ngx_http_userid_filter_module,
    &ngx_http_headers_filter_module,
    &ngx_http_set_misc_module,
    &ngx_http_echo_module,
    &ngx_http_copy_filter_module,
    &ngx_http_range_body_filter_module,
    &ngx_http_not_modified_filter_module,
    NULL
};
        可以看到ngx_http_write_filter_module和ngx_http_header_filter_module两个模块是最靠前的两个filter模块,所以它们处于filter链的最末尾,也就是最后执行。这两个模块就是用来输出header和body的,下面具体分析一下ngx_http_write_filter_module,ngx_http_header_filter_module与之类似。

 

        ngx_http_write_filter_module模块用于输出响应体,在调用时需要传入两个参数ngx_request_t和ngx_chain_t,第一个就是请求,第二个参数是输出内容的buffer链表,通过buffer的last字段标识最后一个buffer。这个模块的filter函数是ngx_http_write_filter,完成的功能很简单就是遍历buffer链表,然后输出响应内容。这里还需要注意一点,ngx_request_t有个out字段用来保存上一次没有发送的chain,当接收到新chain时,需要将新chain连接到旧chain。下面看一下代码。

 

[cpp] view plaincopyprint?
  1. c = r->connection; 
  2.  
  3. if (c->error) { 
  4.     return NGX_ERROR; 
  5.  
  6. size = 0;       /* 待输出的内容的大小 */ 
  7. flush = 0;      /* 是否需要flush */ 
  8. last = 0;       /* 是否是最后一个buffer */ 
  9. ll = &r->out;    /* 保存上次没有输出的ngx_buf_t */ 
    c = r->connection;

    if (c->error) {
        return NGX_ERROR;
    }

    size = 0;		/* 待输出的内容的大小 */
    flush = 0;		/* 是否需要flush */
    last = 0;		/* 是否是最后一个buffer */
    ll = &r->out;	/* 保存上次没有输出的ngx_buf_t */
        这里对使用的变量初始化。
[cpp] view plaincopyprint?
  1. /* find the size, the flush point and the last link of the saved chain */ 
  2. for (cl = r->out; cl; cl = cl->next) { 
  3.     ll = &cl->next; 
  4.  
  5.     ngx_log_debug7(NGX_LOG_DEBUG_EVENT, c->log, 0, 
  6.                    "write old buf t:%d f:%d %p, pos %p, size: %z " 
  7.                    "file: %O, size: %z"
  8.                    cl->buf->temporary, cl->buf->in_file, 
  9.                    cl->buf->start, cl->buf->pos, 
  10.                    cl->buf->last - cl->buf->pos, 
  11.                    cl->buf->file_pos, 
  12.                    cl->buf->file_last - cl->buf->file_pos); 
  13.  
  14.     // ……  
  15. dif 
  16.  
  17.     size += ngx_buf_size(cl->buf); 
  18.  
  19.     if (cl->buf->flush || cl->buf->recycled) { 
  20.         flush = 1; 
  21.     } 
  22.  
  23.     if (cl->buf->last_buf) { 
  24.         last = 1; 
  25.     } 
    /* find the size, the flush point and the last link of the saved chain */
    for (cl = r->out; cl; cl = cl->next) {
        ll = &cl->next;

        ngx_log_debug7(NGX_LOG_DEBUG_EVENT, c->log, 0,
                       "write old buf t:%d f:%d %p, pos %p, size: %z "
                       "file: %O, size: %z",
                       cl->buf->temporary, cl->buf->in_file,
                       cl->buf->start, cl->buf->pos,
                       cl->buf->last - cl->buf->pos,
                       cl->buf->file_pos,
                       cl->buf->file_last - cl->buf->file_pos);

 #if 1
        // ……
 #endif

        size += ngx_buf_size(cl->buf);

        if (cl->buf->flush || cl->buf->recycled) {
            flush = 1;
        }

        if (cl->buf->last_buf) {
            last = 1;
        }
    }
        这段代码统计上次没有发送的chain的大小,以及是否需要flush还有是不是最后一个buffer。
[cpp] view plaincopyprint?
  1.     /* add the new chain to the existent one */ 
  2.  
  3.     for (ln = in; ln; ln = ln->next) { 
  4.         cl = ngx_alloc_chain_link(r->pool); 
  5.         if (cl == NULL) { 
  6.             return NGX_ERROR; 
  7.         } 
  8.  
  9.         cl->buf = ln->buf; 
  10.         /* ll原本保存的是old buf的最后一个节点的next的地址,这里会将new buf连接到old buf后 */ 
  11.         *ll = cl; 
  12.         ll = &cl->next; 
  13.  
  14.         ngx_log_debug7(NGX_LOG_DEBUG_EVENT, c->log, 0, 
  15.                        "write new buf t:%d f:%d %p, pos %p, size: %z " 
  16.                        "file: %O, size: %z"
  17.                        cl->buf->temporary, cl->buf->in_file, 
  18.                        cl->buf->start, cl->buf->pos, 
  19.                        cl->buf->last - cl->buf->pos, 
  20.                        cl->buf->file_pos, 
  21.                        cl->buf->file_last - cl->buf->file_pos); 
  22.  
  23. #if 1  
  24.         // ……  
  25. #endif  
  26.  
  27.         size += ngx_buf_size(cl->buf); 
  28.  
  29.         if (cl->buf->flush || cl->buf->recycled) { 
  30.             flush = 1; 
  31.         } 
  32.  
  33.         /* 需要输出的最后一个buf */ 
  34.         if (cl->buf->last_buf) { 
  35.             last = 1; 
  36.         } 
  37.     } 
    /* add the new chain to the existent one */

    for (ln = in; ln; ln = ln->next) {
        cl = ngx_alloc_chain_link(r->pool);
        if (cl == NULL) {
            return NGX_ERROR;
        }

        cl->buf = ln->buf;
        /* ll原本保存的是old buf的最后一个节点的next的地址,这里会将new buf连接到old buf后 */
        *ll = cl;
        ll = &cl->next;

        ngx_log_debug7(NGX_LOG_DEBUG_EVENT, c->log, 0,
                       "write new buf t:%d f:%d %p, pos %p, size: %z "
                       "file: %O, size: %z",
                       cl->buf->temporary, cl->buf->in_file,
                       cl->buf->start, cl->buf->pos,
                       cl->buf->last - cl->buf->pos,
                       cl->buf->file_pos,
                       cl->buf->file_last - cl->buf->file_pos);

#if 1
        // ……
#endif

        size += ngx_buf_size(cl->buf);

        if (cl->buf->flush || cl->buf->recycled) {
            flush = 1;
        }

        /* 需要输出的最后一个buf */
        if (cl->buf->last_buf) {
            last = 1;
        }
    }
        将新chain添加的旧chain后,并统计size、flush和last。         接下来就是对chain进行处理,这会涉及一些标记,先来处理一下它们:

 

        1. clcf->postpone_output:由于处理postpone_output指令,用于设置延时输出的阈值。比如指令“postpone s”,当输出内容的size小于s,并且不是最后一个buffer,也不需要flush,那么就延时输出。

        2. c->write->delayed:表示当前连接是否因为某种原因需要延时输出,比如超过发送速率限制等,只有在其他地方取消delayed标记后才能继续输出。在delayed设置的情况下,需要设置c->buffered,并返回NGX_AGAIN。

        3. c->buffered:在buffer没有输出完的情况下,标记具体在哪个模块被buffer住了。buffered的取值:

 

[cpp] view plaincopyprint?
  1. #define NGX_HTTP_LOWLEVEL_BUFFERED         0xf0  
  2. #define NGX_HTTP_WRITE_BUFFERED            0x10  
  3. #define NGX_HTTP_GZIP_BUFFERED             0x20  
  4. #define NGX_HTTP_SSI_BUFFERED              0x01  
  5. #define NGX_HTTP_SUB_BUFFERED              0x02  
  6. #define NGX_HTTP_COPY_BUFFERED             0x04 
#define NGX_HTTP_LOWLEVEL_BUFFERED         0xf0
#define NGX_HTTP_WRITE_BUFFERED            0x10
#define NGX_HTTP_GZIP_BUFFERED             0x20
#define NGX_HTTP_SSI_BUFFERED              0x01
#define NGX_HTTP_SUB_BUFFERED              0x02
#define NGX_HTTP_COPY_BUFFERED             0x04
        4. r->limit_rate:对应limit_rate指令,表示request的发送速率限制值。一般地,通过这个值去设置c->write->delayed,当发送速率超过limit时就会设置c->write->delayed,这种情况下就会延迟发送,从而降低request的发送速率。

 

 

[cpp] view plaincopyprint?
  1. /*
  2. * avoid the output if there are no last buf, no flush point,
  3. * there are the incoming bufs and the size of all bufs
  4. * is smaller than "postpone_output" directive
  5. */ 
  6.  
  7. /* 在不是最后一个buf,并且不需要flush的情况下,postpone_output指令可以设置当响应内容小于某个值时,延时输出 */ 
  8. if (!last && !flush && in && size < (off_t) clcf->postpone_output) { 
  9.     return NGX_OK; 
  10.  
  11. /* 请求被delay,设置buffered标记,直接返回 */ 
  12. if (c->write->delayed) { 
  13.     c->buffered |= NGX_HTTP_WRITE_BUFFERED; 
  14.     return NGX_AGAIN; 
  15.  
  16. /* 输出内容大小为0,并且没有设置buffered标记,进入清理工作 */ 
  17. if (size == 0 && !(c->buffered & NGX_LOWLEVEL_BUFFERED)) { 
  18.  
  19.     /* 如果是最后一个标记,清空buffered标记 */ 
  20.     if (last) { 
  21.         r->out = NULL; 
  22.         c->buffered &= ~NGX_HTTP_WRITE_BUFFERED; 
  23.  
  24.         return NGX_OK; 
  25.     } 
  26.  
  27.     /* 如果需要flush */ 
  28.     if (flush) { 
  29.  
  30.         /* 这块不清楚干什么用的 */ 
  31.         do
  32.             r->out = r->out->next; 
  33.         } while (r->out); 
  34.  
  35.         /* 清空buffered标记 */ 
  36.         c->buffered &= ~NGX_HTTP_WRITE_BUFFERED; 
  37.  
  38.         return NGX_OK; 
  39.     } 
  40.  
  41.     ngx_log_error(NGX_LOG_ALERT, c->log, 0, 
  42.                   "the http output chain is empty"); 
  43.  
  44.     ngx_debug_point(); 
  45.  
  46.     return NGX_ERROR; 
  47.  
  48. /* 设置了limit rate */ 
  49. if (r->limit_rate) { 
  50.     limit = r->limit_rate * (ngx_time() - r->start_sec + 1) 
  51.             - (c->sent - clcf->limit_rate_after); 
  52.  
  53.     /* 超出发送速率限制 */ 
  54.     if (limit <= 0) { 
  55.         /* 设置delayed标记,延迟发送请求 */ 
  56.         c->write->delayed = 1; 
  57.         ngx_add_timer(c->write, 
  58.                       (ngx_msec_t) (- limit * 1000 / r->limit_rate + 1)); 
  59.  
  60.         /* 设置buffered标记 */ 
  61.         c->buffered |= NGX_HTTP_WRITE_BUFFERED; 
  62.  
  63.         return NGX_AGAIN; 
  64.     } 
  65.  
  66. } elseif (clcf->sendfile_max_chunk) { 
  67.     /* sendfile用的的limit */ 
  68.     limit = clcf->sendfile_max_chunk; 
  69.  
  70. } else
  71.     limit = 0; 
    /*
     * avoid the output if there are no last buf, no flush point,
     * there are the incoming bufs and the size of all bufs
     * is smaller than "postpone_output" directive
     */

    /* 在不是最后一个buf,并且不需要flush的情况下,postpone_output指令可以设置当响应内容小于某个值时,延时输出 */
    if (!last && !flush && in && size < (off_t) clcf->postpone_output) {
        return NGX_OK;
    }

    /* 请求被delay,设置buffered标记,直接返回 */
    if (c->write->delayed) {
        c->buffered |= NGX_HTTP_WRITE_BUFFERED;
        return NGX_AGAIN;
    }

    /* 输出内容大小为0,并且没有设置buffered标记,进入清理工作 */
    if (size == 0 && !(c->buffered & NGX_LOWLEVEL_BUFFERED)) {

    	/* 如果是最后一个标记,清空buffered标记 */
        if (last) {
            r->out = NULL;
            c->buffered &= ~NGX_HTTP_WRITE_BUFFERED;

            return NGX_OK;
        }

        /* 如果需要flush */
        if (flush) {

        	/* 这块不清楚干什么用的 */
            do {
                r->out = r->out->next;
            } while (r->out);

            /* 清空buffered标记 */
            c->buffered &= ~NGX_HTTP_WRITE_BUFFERED;

            return NGX_OK;
        }

        ngx_log_error(NGX_LOG_ALERT, c->log, 0,
                      "the http output chain is empty");

        ngx_debug_point();

        return NGX_ERROR;
    }

    /* 设置了limit rate */
    if (r->limit_rate) {
        limit = r->limit_rate * (ngx_time() - r->start_sec + 1)
                - (c->sent - clcf->limit_rate_after);

        /* 超出发送速率限制 */
        if (limit <= 0) {
        	/* 设置delayed标记,延迟发送请求 */
            c->write->delayed = 1;
            ngx_add_timer(c->write,
                          (ngx_msec_t) (- limit * 1000 / r->limit_rate + 1));

            /* 设置buffered标记 */
            c->buffered |= NGX_HTTP_WRITE_BUFFERED;

            return NGX_AGAIN;
        }

    } else if (clcf->sendfile_max_chunk) {
    	/* sendfile用的的limit */
        limit = clcf->sendfile_max_chunk;

    } else {
        limit = 0;
    }
        这段代码根据上述标记进行一些处理,具体看注释。接下来就是发送数据的过程以及后续处理。
[cpp] view plaincopyprint?
  1. sent = c->sent; 
  2.  
  3. ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, 
  4.                "http write filter limit %O", limit); 
  5.  
  6. /* send_chain返回的是没有发完的chain */ 
  7. chain = c->send_chain(c, r->out, limit); 
  8.  
  9. ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, 
  10.                "http write filter %p", chain); 
  11.  
  12. if (chain == NGX_CHAIN_ERROR) { 
  13.     c->error = 1; 
  14.     return NGX_ERROR; 
  15.  
  16. /* 对limit_rate处理,可能会设置delayed标记 */ 
  17. if (r->limit_rate) { 
  18.  
  19.     nsent = c->sent; 
  20.  
  21.     if (clcf->limit_rate_after) { 
  22.  
  23.         sent -= clcf->limit_rate_after; 
  24.         if (sent < 0) { 
  25.             sent = 0; 
  26.         } 
  27.  
  28.         nsent -= clcf->limit_rate_after; 
  29.         if (nsent < 0) { 
  30.             nsent = 0; 
  31.         } 
  32.     } 
  33.  
  34.     delay = (ngx_msec_t) ((nsent - sent) * 1000 / r->limit_rate + 1); 
  35.  
  36.     if (delay > 0) { 
  37.         c->write->delayed = 1; 
  38.         ngx_add_timer(c->write, delay); 
  39.     } 
  40.  
  41. } elseif (c->write->ready 
  42.            && clcf->sendfile_max_chunk 
  43.            && (size_t) (c->sent - sent) 
  44.                   >= clcf->sendfile_max_chunk - 2 * ngx_pagesize) 
  45.     c->write->delayed = 1; 
  46.     ngx_add_timer(c->write, 1); 
  47.  
  48. /* 释放已经发送的chain的内存 */ 
  49. for (cl = r->out; cl && cl != chain; /* void */) { 
  50.     ln = cl; 
  51.     cl = cl->next; 
  52.     ngx_free_chain(r->pool, ln); 
  53.  
  54. /* 重新赋值尚未发送的chain */ 
  55. r->out = chain; 
  56.  
  57. /* 如果chain不为空,那么buf没有发送完,需要设置buffered标记,并返回NGX_AGAIN */ 
  58. if (chain) { 
  59.     c->buffered |= NGX_HTTP_WRITE_BUFFERED; 
  60.     return NGX_AGAIN; 
  61.  
  62. /* 如果已经没有未发送的chain,就清空buffered标记 */ 
  63. c->buffered &= ~NGX_HTTP_WRITE_BUFFERED; 
  64.  
  65. /* 如果其他filter模块buffer了chain并且postponed为NULL,那么返回NGX_AGAIN,需要继续处理buf */ 
  66. if ((c->buffered & NGX_LOWLEVEL_BUFFERED) && r->postponed == NULL) { 
  67.     return NGX_AGAIN; 
  68.  
  69. return NGX_OK; 
    sent = c->sent;

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
                   "http write filter limit %O", limit);

    /* send_chain返回的是没有发完的chain */
    chain = c->send_chain(c, r->out, limit);

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
                   "http write filter %p", chain);

    if (chain == NGX_CHAIN_ERROR) {
        c->error = 1;
        return NGX_ERROR;
    }

    /* 对limit_rate处理,可能会设置delayed标记 */
    if (r->limit_rate) {

        nsent = c->sent;

        if (clcf->limit_rate_after) {

            sent -= clcf->limit_rate_after;
            if (sent < 0) {
                sent = 0;
            }

            nsent -= clcf->limit_rate_after;
            if (nsent < 0) {
                nsent = 0;
            }
        }

        delay = (ngx_msec_t) ((nsent - sent) * 1000 / r->limit_rate + 1);

        if (delay > 0) {
            c->write->delayed = 1;
            ngx_add_timer(c->write, delay);
        }

    } else if (c->write->ready
               && clcf->sendfile_max_chunk
               && (size_t) (c->sent - sent)
                      >= clcf->sendfile_max_chunk - 2 * ngx_pagesize)
    {
        c->write->delayed = 1;
        ngx_add_timer(c->write, 1);
    }

    /* 释放已经发送的chain的内存 */
    for (cl = r->out; cl && cl != chain; /* void */) {
        ln = cl;
        cl = cl->next;
        ngx_free_chain(r->pool, ln);
    }

    /* 重新赋值尚未发送的chain */
    r->out = chain;

    /* 如果chain不为空,那么buf没有发送完,需要设置buffered标记,并返回NGX_AGAIN */
    if (chain) {
        c->buffered |= NGX_HTTP_WRITE_BUFFERED;
        return NGX_AGAIN;
    }

    /* 如果已经没有未发送的chain,就清空buffered标记 */
    c->buffered &= ~NGX_HTTP_WRITE_BUFFERED;

    /* 如果其他filter模块buffer了chain并且postponed为NULL,那么返回NGX_AGAIN,需要继续处理buf */
    if ((c->buffered & NGX_LOWLEVEL_BUFFERED) && r->postponed == NULL) {
        return NGX_AGAIN;
    }

    return NGX_OK;
        上面就是ngx_http_write_filter的处理过程,ngx_http_header_filter与之类似,只是它处理的是响应头,然后在最后也是通过ngx_http_write_filter输出。

转载于:https://www.cnblogs.com/wenchuanchuangz/archive/2012/11/15/2772379.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值