文档章节

Nginx filter 模块解析

mickelfeng
 mickelfeng
发布于 2012/12/28 11:03
字数 1843
阅读 141
收藏 4
点赞 0
评论 0

我们知道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
博文 2557
码字总数 554045
作品 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
Apache和Nginx运行原理解析

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

小杨_Ivan
2017/02/09
0
0
Nginx编译安装第三方模块http_substitutions_filter_module

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

adbug
2016/02/21
206
0
ngx lua模块源码简单解析

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

epiclight
2015/06/03
0
0
【技术干货】听阿里云CDN安防技术专家金九讲tengine+lua开发

一、介绍 二、安装 三、运行 四、开发 1.介绍 Tengine:轻量级、高性能、高并发、配置化、模块化、可扩展、可移植的Web和反向代理 服务器,Tengine是nginx超集,但做了很多优化,包含了很多比...

樰篱说
2017/08/11
0
0
Nginx 第三方模块-漫谈缘起

本文的部分内容和图片摘录于http://tengine.taobao.org/download/nginx@taobao.pdf 为什么要使用nginx模块?我个人觉得taobao这个ppt来做说明是最好不过了 从web服务器结构开始说起: 第一个...

王二狗子11
01/08
0
0
Nginx模块开发指南中文版

1. 预备知识 你应当比较熟悉C语言。不光是“C-语法",你起码还得知道结构体和预处理指令,同时保证看到指针和函数引用出现时心里不会发毛。否则的话,就算信春哥也是没用的,看看K&R吧。 你得...

红薯
2010/03/08
21.2K
11
Nginx下编写c模块来阻止HTTP代理服务器的访问

Nginx下编写c模块来阻止HTTP代理服务器的访问 在这篇使用Apache来阻止http代理服务器的访问文章里,我们使用了Apache的mod_rewrite来判断访客的http head头,如果看到了有代理的迹象,就阻止...

mickelfeng
2015/10/12
289
1
nginx源码分析——filter模块

filter模块简介 在nginx中,明确将HTTP响应分为两个部分——HTTP头部和HTTP包体,而filter模块的主要作用就是对HTTP响应信息进行加工处理。filter模块在NGXHTTPCONTENTPHASE阶段参与处理(H...

hncscwc
2016/10/28
70
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

20位活跃在Github上的国内技术大牛 leij 何小鹏 亚信

本文列举了20位在Github上非常活跃的国内大牛,看看其中是不是很多熟悉的面孔? 1. lifesinger(玉伯) Github主页: https://github.com/lifesinger 微博:@ 玉伯也叫射雕 玉伯(王保平),...

海博1600
3分钟前
0
0
高性能服务器本质论

一 服务器分类 从软件性能角度,高性能服务器分:cpu密集型服务器/IO密集型服务器 (1)CPU密集型:该类服务器没有对io的访问/没有同步点,性能瓶颈在于对cpu的充分利用。 典型的如转发服务器/...

码代码的小司机
4分钟前
0
0
Mybatis收集配置

一、Mybatis取Clob数据 1、Mapper.xml配置 <resultMap type="com.test.User" id="user"> <result column="id" property="id"/> <result column="json_data" property="jsonData" ......

星痕2018
29分钟前
0
0
centos7设置以多用户模式启动

1、旧版本linux系统修改inittab文件,在新版本执行vi /etc/inittab 会有以下提示 # inittab is no longer used when using systemd. # # ADDING CONFIGURATION HERE WILL HAVE NO EFFECT ON......

haha360
今天
0
0
OSChina 周日乱弹 —— 局长:怕你不爱我

Osc乱弹歌单(2018)请戳(这里) 【今日歌曲】 @ andonny :分享周二珂的单曲《孤独她呀》 《孤独她呀》- 周二珂 手机党少年们想听歌,请使劲儿戳(这里) @孤星闵月 :没事干,看一遍红楼梦...

小小编辑
今天
158
9
Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式 Factory工厂模式 Singleton单例模式 Delegate委派模式 Strategy策略模式 Prototype原型模式 Template模板模式 Spring5 beans 接口实例化 代理Bean操作 ...

小致dad
今天
0
0
SpringBoot | 第十章:Swagger2的集成和使用

前言 前一章节介绍了mybatisPlus的集成和简单使用,本章节开始接着上一章节的用户表,进行Swagger2的集成。现在都奉行前后端分离开发和微服务大行其道,分微服务及前后端分离后,前后端开发的...

oKong
今天
10
0
Python 最小二乘法 拟合 二次曲线

Python 二次拟合 随机生成数据,并且加上噪声干扰 构造需要拟合的函数形式,使用最小二乘法进行拟合 输出拟合后的参数 将拟合后的函数与原始数据绘图后进行对比 import numpy as npimport...

阿豪boy
今天
17
0
云拿 无人便利店

附近(上海市-航南路)开了家无人便利店.特意进去体验了一下.下面把自己看到的跟大家分享下. 经得现场工作人员同意后拍了几张照片.从外面看是这样.店门口的指导里强调:不要一次扫码多个人进入....

周翔
昨天
1
0
Java设计模式学习之工厂模式

在Java(或者叫做面向对象语言)的世界中,工厂模式被广泛应用于项目中,也许你并没有听说过,不过也许你已经在使用了。 简单来说,工厂模式的出现源于增加程序序的可扩展性,降低耦合度。之...

路小磊
昨天
248
1

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部