Nginx深入详解之过滤模块

一、模块简介

    过滤(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,响应头和响应体过滤函数的执行顺序如下图:

三、模块编译

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

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中指针的用法:

五、过滤函数

 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_AGAIN,分别表示处理成功、失败和未完成。

    ngx_http_header_filter_module过滤模块把所有的HTTP头组合成一个完成的buffer,最终由ngx_http_write_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_filter模块中,Nginx会将in输出链拷贝到r->out输出链的末尾,然后调用sendfile或者writev接口输出。由于Nginx是非阻塞的socket接口,写操作并不一定会成功,可能会有部分数据还残存在r->out。在下次的调用中,Nginx会继续尝试发送,直至成功。

六、子请求

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

    关键点是在postpone_filter模块中,它会拼接主请求和子请求的响应内容。r->postponed按次序保存有父请求和子请求,它是一个链表,如果前面一个请求未完成,那么后一个请求内容就不会输出。当前一个请求完成时并输出时,后一个请求才可输出,当所有的子请求都完成时,所有的响应内容也就输出完毕了。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值