Chinaunix首页 | 论坛 | 博客
  • 博客访问: 5644158
  • 博文数量: 291
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 7924
  • 用 户 组: 普通用户
  • 注册时间: 2016-07-06 14:28
个人简介

阿里巴巴是个快乐的青年

文章分类

全部博文(291)

文章存档

2018年(21)

2017年(4)

2016年(5)

2015年(17)

2014年(68)

2013年(174)

2012年(2)

分类: Web开发

2013-11-10 20:46:10

一、模块简介       
        过滤(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,然后各个模块依次执行。所有第三方模块只能加入到copy_filter和headers_filter模块之间执行。每个filter模块的处理函数赋值给全局变量ngx_http_top_header_filter,而前一个filter模块的处理函数赋值给局部变量ngx_http_next_header_filter,响应头和响应体过滤函数的执行顺序如下图:

图1 header_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是该模块的源代码。

四、输出内容
        基于Nginx流式输出模式,在过滤模块中,所有输出内容都是通过一条单向链表组成,每次Nginx都是读到一部分的内容就放到链表,然后输出出去。单向链表结构如下:
        typedef struct ngx_chain_s ngx_chain_t;
        struct ngx_chain_s {
            ngx_buf_t *buf;
            ngx_chain_t *next;
        };
        一般buffer结构体可以表示一块内存,内存的起始和结束地址分别用start和end表示,pos和last表示实际的内容。如果内容已经处理过了,pos的位置就可以往后移动。如果读取到新的内容,last的位置就会往后移动。所以buffer可以在多次调用过程中使用。如果last等于end,就说明这块内存已经用完了。如果pos等于last,说明内存已经处理完了。下面是一个简单地示意图,说明buffer中指针的用法:

图2 buffer内存结构

五、过滤函数
1、响应头过滤函数
        响应头过滤函数的主要用处是处理HTTP响应的头,可以根据实际情况对于响应头进行修改或者添加删除。响应头过滤函数先于响应体过滤函数,而且只调用一次,所以一般是做过滤模块的初始化工作,响应头过滤函数的入口如下:
        ngx_int_t ngx_http_send_header(ngx_http_request_t *r)
        {
            ...
            return ngx_http_top_header_filter(r);
        }
        该函数在向客户端发送回复的时候调用,返回值一般为NGX_OK、NGX_ERROR和NGX_AGIN,分别表示处理成功、失败和未完成。
        ngx_http_header_filter_module过滤模块把所有的HTTP头组合成一个完成的buffer,最终由ngx_http_write_filter_module过滤模块把buffer输出。
2、响应体过滤函数
        响应体过滤函数是过滤响应主体的函数。对于每个请求,函数ngx_http_top_body_filter可能会被执行多次,它的入口函数如下:
        ngx_int_t ngx_http_output_filter(ngx_http_request_t *r, ngx_chain_t *in)
        {
            ngx_int_t rc;
            ngx_connection_t *c;
            
            c = r->connection;
            
            rc = ngx_http_top_body_filter(r, in);

            if (rc == NGX_ERROR) {
                /* NGX_ERROR may be returned by any filter */
                c->error = 1;
            }

            return rc;
        }
        对于整个请求的处理阶段来说,ngx_http_output_filter的作用就是把响应内存过滤,然后发给客户端,具体模块的响应体过滤函数的格式会类似如下:
        static int ngx_http_example_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
        {
            ...
            return ngx_http_next_body_filter(r, in);
        }
        该函数的返回值一般是NGX_OK、NGX_ERROR和NGX_AGAIN,分别表示处理成功、失败和未完成。
        响应的主体内容就存于单链表in,链表一般不会太长,有时in参数可能为NULL。in中存有buf结构体中,对于静态文件,这个buf大小默认是32K;对于反向代理的应用,这个buf可能是4k或者8k。为了保持内存的低消耗,Nginx一般不会分配过大的内存,处理的原则是收到一定的数据,就发送出去。
        在响应体过滤模块中,尤其要注意的是buf的标志位,具体可以参看图2。如果buf中包含last标志,说明是最后一块buf,可以直接输出并结束请求了。如果有flush标志,说明这块buf需要马上输出,不能缓存。如果整块buffer经过处理完以后,没有数据了,你可以把buffer的sync标志置上,表示只是同步的用处。当所有的过滤模块都处理完毕时,在最后的write_fitler模块中,Nginx会将in输出链拷贝到r->out输出链的末尾,然后调用sendfile或者writev接口输出。由于Nginx是非阻塞的socket接口,写操作并不一定会成功,可能会有部分数据还残存在r->out。在下次的调用中,Nginx会继续尝试发送,直至成功。

六、子请求
        Nginx过滤模块一大特色就是可以发出子请求,即在过滤响应内容的时候,你可以发送新的请求,Nginx会根据你调用的先后顺序,将多个回复的内容拼接成正常的响应主体。一个简单的例子可以参考addtion模块。当Nginx发出子请求时,就会调用ngx_http_subrequest函数,将子请求插入父请求的r->postponed链表中。子请求会在主请求执行完毕时获得依次调用。子请求同样会有一个请求所有的生存期和处理过程,也会进入过滤模块流程。
        关键点是在postpone_filter模块中,它会拼接主请求和子请求的响应内容。r->postponed按次序保存有父请求和子请求,它是一个链表,如果前面一个请求未完成,那后一个请求内容就不会输出。当前一个请求完成时并输出时,后一个请求才可输出,当所有的子请求都完成时,所有的响应内容也就输出完毕了。


阅读(5154) | 评论(1) | 转发(2) |
给主人留下些什么吧!~~

scq2099yt2013-11-10 20:46:27

文明上网,理性发言...