文档章节

Nginx filter 模块解析

mickelfeng
 mickelfeng
发布于 2012/12/28 11:03
字数 1843
阅读 143
收藏 4

我们知道nginx很多功能都是通过filter模块来实现的,如:替换contentsub modulecontent压缩的gzip module等。接下去我们看看nginx是怎样处理filter模块的。

Nginx filter module所有的代码都在src\http\module\目录中,打开可以看到nginx拥有十几个filter module,以xxx_filter_module.c结尾的源文件便是filter module

Http请求分为HeaderContent两部分,粗略的看代码可以发现,nginxHttp HeaderContent使用了分别的filter函数进行处理。举个例子,我们看gzip filter module的代码可以发现,ngx_http_gzip_header_filter函数便是nginx gzipHttp Header的处理,同样ngx_http_gzip_body_filter函数便是对Http Content的处理了。

nginx调用这十几个filter函数的流程便是本节内容所要讨论的。

 

在源码配置的过程中,会生成/objs/ngx_modules.c文件,其中定义了一个名为ngx_modules的数组,其中的内容便是nginx所有的module,源码为:

ngx_module_t *ngx_modules[] = {

    …………

    &ngx_http_proxy_module,

    &ngx_http_memcached_module,

    &ngx_http_upstream_ip_hash_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_sub_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

};

    细心的朋友可能会发现,从ngx_http_write_filter_module开始,下面的全为filter_module结尾,没错,下面以filter_module结尾的便是nginx filter module

我们首先通过较熟悉的gzip filter module入手,我们先看init函数ngx_http_gzip_filter_init,源码为:

static ngx_int_t

ngx_http_gzip_filter_init(ngx_conf_t *cf)

{

    // 将 gzip head filter 串到整个链表的头部

    ngx_http_next_header_filter = ngx_http_top_header_filter;

    ngx_http_top_header_filter = ngx_http_gzip_header_filter;

 

    //  gzip body filter 串到整个链表的头部

    ngx_http_next_body_filter = ngx_http_top_body_filter;

    ngx_http_top_body_filter = ngx_http_gzip_body_filter;

 

    return NGX_OK;

}

    代码其实挺简单的,做了两个链表串接操作。我们先看headerngx_http_top_header_filter为指向整个链表头结点的函数指针(注意:此处为函数指针),此处将next指针指向头指针,将头指针指向gzip header filter,然后在函数ngx_http_gzip_header_filter处理完成后,最终又会调用ngx_http_next_header_filter,这样就将gzip header filter结点串到链表的前面了。Body 处理过程类似,不予重复。

    从如上代码分析可以看出,filter module都是串在链表前面的,所以ngx_modules数组中ngx_http_not_modified_filter_module应该是最后一个被串入的,也就是说,此模块应该在链表的最前面。

到此为止,我们知道nginx所有filter module都是通过链表串接起来的,并且数组中最后面的元素在链表的第一个结点中。接下去我们看看nginx怎样将整个链表执行起来。

ngx_http_core_module.cngx_http_send_header便是整个filter链的开始,在其中调用了ngx_http_top_header_filter函数,来启动整个header filter。同样ngx_http_output_filter便是启动整个body filter的函数。当整个head链处理完成之后,再行处理body链。

 

我们通过nginx head filter中第一个被处理的filter来仔细分析一下。由于是逆向串入的,所以ngx_http_not_modified_filter_module就是第一个被处理的filter了。

我们还是先看init函数ngx_http_not_modified_filter_init,源码为:

static ngx_int_t

ngx_http_not_modified_filter_init(ngx_conf_t *cf)

{

    ngx_http_next_header_filter = ngx_http_top_header_filter;

    ngx_http_top_header_filter = ngx_http_not_modified_header_filter;

 

    return NGX_OK;

}

非常简单,仅仅将head filter模块串入,此模块没有body filter,此处分析就先略过去了,正好简化分析。

我们再看刚才初始化后的入口函数ngx_http_not_modified_header_filter,源码为:

static ngx_int_t

ngx_http_not_modified_header_filter(ngx_http_request_t *r)

{

    if (r->headers_out.status != NGX_HTTP_OK

        || r != r->main

        || r->headers_out.last_modified_time == -1)

    {

        return ngx_http_next_header_filter(r);

    }

 

    // 判断是否应该返回 Http 412Precondition Failed

    if (r->headers_in.if_unmodified_since) {

        return ngx_http_test_precondition(r);

    }

 

    // 判断是否应该返回 Http 304Not Modified

    if (r->headers_in.if_modified_since) {

        return ngx_http_test_not_modified(r);

    }

 

    return ngx_http_next_header_filter(r);

}

    代码也挺简单,首先判断Http状态、一些Flag等信息,如果没有通过判断规则,则直接跳入下一个head filter。然后判断是否没达到返回条件,即:nginx是否应该返回Http 412Precondition Failed)状态码。然后判断是否应该返回Http 304Not Modified)状态码。最终如果都没判断通过的话,则直接跳入下一个head filter的处理了。

    我们做个假设r->headers_in.if_modify_since不为NULL,则进入ngx_http_test_not_modified,在此函数中,如果执行成功,则将http状态值设置为304,最终跳到下一个head filter

    回过头来再看看,由于此filter仅仅处理Http head信息,无需处理任何Http Content,所以当然就没有类似 body_filter的函数了。

 

我们接下去看第二个将要处理的模块,即ngx_modules中的倒数第二项。在处理倒数第二项时,其仅仅对Http Content做了处理,处理的流程类似于前面的head分析,此处就不详细解释了。

最终,我们可以看一下ngx_http_header_filter_modulenginx filter moduleHttp Header的最后处理。先看init函数ngx_http_header_filter_init,由于这个是第一个结点,在这之前并不存在head filter结点,所以仅仅需要赋值ngx_http_top_header_filter即可。在此head filter中,最终会调用ngx_http_write_filter函数(在ngx_http_header_filter之中)完成整个nginx head filter的处理。

ngx_http_writer_filter_module最终对Http body进行处理,最终会调用send_chain函数(ngx_http_write_filter)完成整个nginx body filter的处理。

 

    到此为止,我们解释了整个filter module链的串接过程,也做出了实例分析。但有个问题一直未曾解释,nginx是怎样调用所有filter moduleinit函数的,因为查看代码知道,每个filterinit函数名不相同,filter module也仅仅存在于一个c文件中,并无对应的头文件。

本篇开头,我们列出过一个结构体,其中包含了所有nginx module的指针,而对应于ngx_http_not_modified_filter_module的是最后一个,此结构体最终的定义在ngx_http_not_modified_filter_module.c文件中,源码为:

ngx_module_t  ngx_http_not_modified_filter_module = {

    NGX_MODULE_V1,

    &ngx_http_not_modified_filter_module_ctx, /* module context */

    NULL,                                  /* module directives */

    NGX_HTTP_MODULE,                       /* module type */

    NULL,                                  /* init master */

    NULL,                                  /* init module */

    NULL,                                  /* init process */

    NULL,                                  /* init thread */

    NULL,                                  /* exit thread */

    NULL,                                  /* exit process */

    NULL,                                  /* exit master */

    NGX_MODULE_V1_PADDING

};

为了说明方便,列出 ngx_module_t 的定义:

typedef struct ngx_module_s      ngx_module_t;

struct ngx_module_s {

    ngx_uint_t            ctx_index;

    ngx_uint_t            index;

 

    ngx_uint_t            spare0;

    ngx_uint_t            spare1;

    ngx_uint_t            spare2;

    ngx_uint_t            spare3;

 

    ngx_uint_t            version;

 

    void                 *ctx;

    ngx_command_t        *commands;

    ngx_uint_t            type;

 

    ngx_int_t           (*init_master)(ngx_log_t *log);

 

    ngx_int_t           (*init_module)(ngx_cycle_t *cycle);

 

    ngx_int_t           (*init_process)(ngx_cycle_t *cycle);

    ngx_int_t           (*init_thread)(ngx_cycle_t *cycle);

    void                (*exit_thread)(ngx_cycle_t *cycle);

    void                (*exit_process)(ngx_cycle_t *cycle);

 

    void                (*exit_master)(ngx_cycle_t *cycle);

 

    uintptr_t             spare_hook0;

    uintptr_t             spare_hook1;

    uintptr_t             spare_hook2;

    uintptr_t             spare_hook3;

    uintptr_t             spare_hook4;

    uintptr_t             spare_hook5;

    uintptr_t             spare_hook6;

    uintptr_t             spare_hook7;

};

可以看到,最终启用的ngx_module_t的数据项为 ngx_module_t[index]::ctx(注意NGX_MODULE_V1是一个宏,对应前面7项),此ctx定义为void*类型,在此filter module中,最终转化为类型ngx_http_module_t,其中的数据为:

typedef struct {

    ngx_int_t   (*preconfiguration)(ngx_conf_t *cf);

    ngx_int_t   (*postconfiguration)(ngx_conf_t *cf);

 

    void       *(*create_main_conf)(ngx_conf_t *cf);

    char       *(*init_main_conf)(ngx_conf_t *cf, void *conf);

 

    void       *(*create_srv_conf)(ngx_conf_t *cf);

    char       *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, void *conf);

 

    void       *(*create_loc_conf)(ngx_conf_t *cf);

    char       *(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf);

} ngx_http_module_t;

 

static ngx_http_module_t  ngx_http_not_modified_filter_module_ctx = {

    NULL,                                  /* preconfiguration */

    ngx_http_not_modified_filter_init,     /* postconfiguration */

 

    NULL,                                  /* create main configuration */

    NULL,                                  /* init main configuration */

 

    NULL,                                  /* create server configuration */

    NULL,                                  /* merge server configuration */

 

    NULL,                                  /* create location configuration */

    NULL                                   /* merge location configuration */

};

至此我们看到了ngx_http_not_modified_filter_init函数,也就是说,这个函数最终可以通过ngx_modules数组来访问并调用,示例代码:

ngx_modules[module index]->ctx->postconfiguration(cf);

    如上module index假设为ngx_http_not_modified_filter_module的数组索引,ngx_modules[module index]->ctx便是ngx_http_not_modified_filter_modulectx,即ngx_http_not_modified_filter_module_ctxngx_modules[module index]->ctx的类型为ngx_http_module_t,可以通过预定义类型来进行访问,所以ngx_modules[module index]->ctx->postconfiguration(cf);便调用到了最终的init函数。

ngx_http.c中有函数ngx_http_block,其中便如上进行调用了,源码片段:

    // 循环遍历整个 ngx_modules 数组

for (m = 0; ngx_modules[m]; m++) {

    //  module 的类型进行判断

        if (ngx_modules[m]->type != NGX_HTTP_MODULE) {

            continue;

        }

      

        // 临时时变量 module 指向 ngx_modules[m]->ctx,即 xxx_module_filter_ctx 函数

        // 对于 ngx_http_not_modified_filter_module 来说,module 即 ngx_http_not_modified_filter_module_ctx

        module = ngx_modules[m]->ctx;

 

        // 判断并调用 postconfiguration 函数,即 xxx_module_filter_init 函数

        // 对于 ngx_http_not_modified_filter_module 来说,即调用了 ngx_http_not_modified_filter_init

        if (module->postconfiguration) {

            if (module->postconfiguration(cf) != NGX_OK) {

                return NGX_CONF_ERROR;

            }

        }

    }

本文转载自:

共有 人打赏支持
mickelfeng

mickelfeng

粉丝 227
博文 2634
码字总数 568692
作品 0
成都
高级程序员
Nginx反向代理并替换内容模块ngx_http_substitutions_filter_modu

====== 以前写过Nginx反向代理通过with-httpsubmodule和substitutions4nginx模块替换正文内容和URL和在军哥lnmp的环境下配置反向代理服务器的方法教程 本教程基于军哥lnmp环境,其他Nginx类同...

adbug
2016/02/16
707
0
Nginx系列教程:nginx_substitutions_filter模块

nginxsubstitutionsfilter 请注意:此模块不是Nginx源的分布,可点击此链接找到安装说明, http://wiki.nginx.org/NginxHttpSubsModule#Installation 。 概述 nginxsubstitutionsfilter 是一...

mickelfeng
2012/12/28
0
0
Nginx编译安装第三方模块http_substitutions_filter_module

ngxhttpsubstitutionsfiltermodule OR HttpSubModule ? 为了应急处理或者一些需要,有时候需要使用Nginx的反向代理某站点,并通过 HttpSubModule 和ngxhttpsubstitutionsfiltermodule 模块替...

adbug
2016/02/21
206
0
Apache和Nginx运行原理解析

Web服务器 Web服务器也称为WWW(WORLD WIDE WEB)服务器,主要功能是提供网上信息浏览服务。 应用层使用HTTP协议。 HTML文档格式。 浏览器统一资源定位器(URL)。 Web服务器常常以B/S(Browser...

小杨_Ivan
2017/02/09
0
0
ngx lua模块源码简单解析

ngx lua模块源码简单解析分类: nginx 2014-07-11 11:45 2097人阅读 评论(0) 收藏 举报nginxlua数据结构架构目录(?)[+]对nginx lua模块的整个流程,原理简单解析。由于nginx lua模块相关配置...

epiclight
2015/06/03
0
0

没有更多内容

加载失败,请刷新页面

加载更多

CentOS7防火墙firewalld操作

firewalld Linux上新用的防火墙软件,跟iptables差不多的工具。 firewall-cmd 是 firewalld 的字符界面管理工具,firewalld是CentOS7的一大特性,最大的好处有两个:支持动态更新,不用重启服...

dingdayu
今天
1
0
关于组件化的最初步

一个工程可能会有多个版本,有国际版、国内版、还有针对各种不同的渠道化的打包版本、这个属于我们日常经常见到的打包差异化版本需求。 而对于工程的开发,比如以前的公司,分成了有三大块业...

DannyCoder
今天
2
0
Spring的Resttemplate发送带header的post请求

private HttpHeaders getJsonHeader() { HttpHeaders headers = new HttpHeaders(); MediaType type = MediaType.parseMediaType("application/json; charset=UTF-8"); ......

qiang123
昨天
3
0
Spring Cloud Gateway 之 Only one connection receive subscriber allowed

都说Spring Cloud Gateway好,我也来试试,可是配置了总是报下面这个错误: java.lang.IllegalStateException: Only one connection receive subscriber allowed. 困扰了我几天的问题,原来...

ThinkGem
昨天
27
0
学习设计模式——观察者模式

1. 认识观察者模式 1. 定义:定义对象之间一种一对多的依赖关系,当一个对象状态发生变化时,依赖该对象的其他对象都会得到通知并进行相应的变化。 2. 组织结构: Subject:目标对象类,会被...

江左煤郎
昨天
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部