17、Nginx 入门指南 - 过滤模块简介

2019-07-03

执行时间和内容

过滤(filter)模块是过滤响应头和内容的模块,可以对回复的头和内容进行处理。它的处理时间在获取回复内容之后,向用户发送响应之前。它的处理过程分为两个阶段,过滤 HTTP 回复的头部和主体,在这两个阶段可以分别对头部和主体进行修改。

在代码中有类似的函数:

ngx_http_top_header_filter(r);
ngx_http_top_body_filter(r, in);

就是分别对头部和主体进行过滤的函数。所有模块的响应内容要返回给客户端,都必须调用这两个接口。

执行顺序

过滤模块的调用是有顺序的,它的顺序在编译的时候就决定了。控制编译的脚本位于 auto/modules 中,当你编译完 Nginx 以后,可以在 objs 目录下面看到一个 ngx_modules.c 的文件。打开这个文件,有类似的代码:

ngx_module_t *ngx_modules[] = {
            ...
            &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_copy_filter_module,
            &ngx_http_range_body_filter_module,
            &ngx_http_not_modified_filter_module,
            NULL
        };

从 write_filter 到 not_modified_filter,模块的执行顺序是反向的。也就是说最早执行的是 not_modified_filter,然后各个模块依次执行。一般情况下,第三方过滤模块的 config 文件会将模块名追加到变量 HTTP_AUX_FILTER_MODULES 中,此时该模块只能加入到 copy_filter 和 headers_filter 模块之间执行。

Nginx 执行的时候是怎么按照次序依次来执行各个过滤模块呢?它采用了一种很隐晦的方法,即通过局部的全局变量。比如,在每个 filter 模块,很可能看到如下代码:

static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;
        static ngx_http_output_body_filter_pt    ngx_http_next_body_filter;

        ...

        ngx_http_next_header_filter = ngx_http_top_header_filter;
        ngx_http_top_header_filter = ngx_http_example_header_filter;

        ngx_http_next_body_filter = ngx_http_top_body_filter;
        ngx_http_top_body_filter = ngx_http_example_body_filter;

ngx_http_top_header_filter 是一个全局变量。当编译进一个 filter 模块的时候,就被赋值为当前 filter 模块的处理函数。而 ngx_http_next_header_filter 是一个局部全局变量,它保存了编译前上一个 filter 模块的处理函数。所以整体看来,就像用全局变量组成的一条单向链表。

每个模块想执行下一个过滤函数,只要调用一下 ngx_http_next_header_filter 这个局部变量。而整个过滤模块链的入口,需要调用 ngx_http_top_header_filter 这个全局变量。ngx_http_top_body_filter 的行为与 header fitler 类似。

响应头和响应体过滤函数的执行顺序如下所示:

这图只表示了 head_filter 和 body_filter 之间的执行顺序,在 header_filter 和 body_filter 处理函数之间,在 body_filter 处理函数之间,可能还有其他执行代码。

模块编译

Nginx 可以方便的加入第三方的过滤模块。在过滤模块的目录里,首先需要加入 config 文件,文件的内容如下:

ngx_addon_name=ngx_http_example_filter_module
HTTP_AUX_FILTER_MODULES="$HTTP_AUX_FILTER_MODULES ngx_http_example_filter_module"
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_example_filter_module.c"

说明把这个名为 ngx_http_example_filter_module 的过滤模块加入,ngx_http_example_filter_module.c 是该模块的源代码。

注意 HTTP_AUX_FILTER_MODULES 这个变量与一般的内容处理模块不同。