pc 移动端网站建设,手机网站域名怎么解析,网页设计html代码大全怎么改颜色,做企业网站有效果吗文章目录过滤模块简介执行时间和内容执行顺序Nginx是怎么按照次序依次来执行各个过滤模块的呢这些过滤模块的简述#xff08;按执行顺序#xff09;模块编译过滤模块分析相关结构体响应头过滤函数响应体过滤函数主要功能介绍发出子请求优化措施过滤模块简介 
执行时间和内容 …
文章目录过滤模块简介执行时间和内容执行顺序Nginx是怎么按照次序依次来执行各个过滤模块的呢这些过滤模块的简述按执行顺序模块编译过滤模块分析相关结构体响应头过滤函数响应体过滤函数主要功能介绍发出子请求优化措施过滤模块简介 
执行时间和内容 过滤模块是过滤响应头和内容的模块。它工作在获取回复内容之后向用户发送响应之前。它的处理过程分为两个阶段过滤HTTP回复的头部和主体在这两个阶段可以分别对头部和主体进行修改。  src/http/ngx_http.c中有对filter函数指针变量的定义 所有模块的响应内容都要调用这两个函数指针指向的函数被这两个函数处理后再返回给客户端。 ngx_http_output_header_filter_pt ngx_http_top_header_filter;ngx_http_output_body_filter_pt ngx_http_top_body_filter;  
执行顺序 
filter的调用顺序在编译的时候就决定了在auto/modules文件中有对其控制编译的脚本。当编译完成后在objs/目录下的ngx_modules.c文件中有一个ngx_module_t类型的数组ngx_modules里面记录着nginx所有使用到的模块我们只关心filter模块 从write_filter到not_modified_filter模块的执行顺序是反向的。也就是说最早执行的是not_modified_filter然后各个模块依次执行。所有第三方的模块只能加入到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类似。 
这些过滤模块的简述按执行顺序 
filter模块描述ngx_http_not_modified_filter_module默认打开如果请求的if-modified-since等于回复的last-modified间值说明回复没有变化清空所有回复的内容返回304ngx_http_range_body_filter_module默认打开只是响应体过滤函数支持range功能如果请求包含range请求那就只发送range请求的一段内容ngx_http_copy_filter_module始终打开只是响应体过滤函数 主要工作是把文件中内容读到内存中以便进行处理ngx_http_headers_filter_module始终打开可以设置expire和Cache-control头可以添加任意名称的头ngx_http_userid_filter_module默认关闭可以添加统计用来识别用户的cookiengx_http_charset_filter_module默认关闭可以添加charset也可以将内容从一种字符集转换到另外一种字符集不支持多字节字符集ngx_http_ssi_filter_module默认关闭过滤SSI请求可以发起子请求去获取include进来的文件ngx_http_postpone_filter_module始终打开用来将子请求和主请求的输出链合并ngx_http_gzip_filter_module默认关闭支持流式的压缩内容ngx_http_range_header_filter_module默认打开只是响应头过滤函数用来解析range头并产生range响应的头ngx_http_chunked_filter_module默认打开对于HTTP/1.1和缺少content-length的回复自动打开ngx_http_header_filter_module始终打开用来将所有header组成一个完整的HTTP头ngx_http_write_filter_module始终打开将输出链拷贝到r-out中然后输出内容
模块编译 
Nginx可以方便的加入第三方的过滤模块。在过滤模块的目录里首先需要加入config文件文件的内容如下 
ngx_addon_namengx_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是该模块的源代码。 
过滤模块分析 
相关结构体 单向链表 ngx_chain_t typedef struct ngx_chain_s           ngx_chain_t;
struct ngx_chain_s {ngx_buf_t    *buf;ngx_chain_t  *next;
};在过滤模块中所有输出的内容都是通过一条单向链表所组成。这种单向链表的设计正好应和了Nginx流式的输出模式。每次Nginx都是读到一部分的内容就放到链表然后输出出去。这种设计的好处是简单非阻塞但是相应的问题就是跨链表的内容操作非常麻烦如果需要跨链表很多时候都只能缓存链表的内容。  单链表负载的就是ngx_buf_t结构体 typedef struct ngx_buf_s  ngx_buf_t;
struct ngx_buf_s {u_char          *pos;		/* 当前buffer真实内容的起始位置 */u_char          *last;		/* 当前buffer真实内容的结束位置 */off_t            file_pos;	/* 在文件中真实内容的起始位置 */off_t            file_last;	 /* 在文件中真实内容的结束位置 */u_char          *start;		/* buffer内存的开始分配的位置 */u_char          *end;		/* buffer内存的结束分配的位置 */ngx_buf_tag_t    tag;		/* buffer属于哪个模块的标志 */ngx_file_t      *file;		/* buffer所引用的文件 */ngx_buf_t       *shadow;	/* 用来引用替换过后的buffer以便当所有buffer输出以后这个影子buffer可以被释放。*//* the bufs content could be changed */unsigned         temporary:1;/** the bufs content is in a memory cache or in a read only memory* and must not be changed*/unsigned         memory:1;/* the bufs content is mmap()ed and must not be changed */unsigned         mmap:1;unsigned         recycled:1;	/* 内存可以被输出并回收 */unsigned         in_file:1;		/* buffer的内容在文件中 */unsigned         flush:1;/* 马上全部输出buffer的内容, gzip模块里面用得比较多 */unsigned         sync:1;/* 基本上是一段输出链的最后一个buffer带的标志标示可以输出有些零长度的buffer也可以置该标志*/unsigned         last_buf:1;	/* 所有请求里面最后一块buffer包含子请求 */unsigned         last_in_chain:1;	/* 当前请求输出链的最后一块buffer */unsigned         last_shadow:1;	/* shadow链里面的最后buffer可以释放buffer了 */unsigned         temp_file:1;	/* 是否是暂存文件 *//* STUB */ int   num;	/* 统计用表示使用次数 */
};一般buffer结构体可以表示一块内存内存的起始和结束地址分别用start和end表示pos和last表示实际的内容。如果内容已经处理过了pos的位置就可以往后移动。如果读取到新的内容last的位置就会往后移动。所以buffer可以在多次调用过程中使用。如果last等于end就说明这块内存已经用完了。如果pos等于last说明内存已经处理完了。  
响应头过滤函数 
响应头过滤函数主要的用处就是处理HTTP响应的头可以根据实际情况对于响应头进行修改或者添加删除。响应头过滤函数先于响应体过滤函数而且只调用一次所以一般可作过滤模块的初始化工作。 
响应头过滤函数的入口只有一个 
ngx_int_t
ngx_http_send_header(ngx_http_request_t *r)
{···return ngx_http_top_header_filter(r);
}该函数向客户端发送回复的时候调用然后按上述的执行顺序。该函数的返回值一般是NGX_OKNGX_ERROR和NGX_AGAIN分别表示处理成功失败和未完成。 可以把HTTP响应头的存储方式想象成一个hash表在Nginx内部可以查找和修改各个响应头ngx_http_header_filter_module过滤模块把所有的HTTP头组合成一个完整的buffer最终ngx_http_write_filter_module过滤模块把buffer输出。 
响应体过滤函数 
响应体过滤函数是过滤响应主体的函数。ngx_http_top_body_filter这个函数每个请求可能会被执行多次它的入口函数是ngx_http_output_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;ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c-log, 0,http output filter \%V?%V\, r-uri, r-args);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可以被一般的静态处理模块调用也可能在upstream模块里面被调用对于整个请求的处理阶段来说他们的用处都是一样的就是把响应内容过滤然后发给客户端。 
具体模块的响应体过滤函数的格式类似这样 
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_OKNGX_ERROR和NGX_AGAIN分别表示处理成功失败和未完成。 
主要功能介绍 响应的主体内容就存于单链表in链表一般不会太长有时in参数可能为NULL。in中存有buf结构体中对于静态文件这个buf大小默认是32K对于反向代理的应用这个buf可能是4k或者8k。为了保持内存的低消耗Nginx一般不会分配过大的内存处理的原则是收到一定的数据就发送出去。一个简单的例子可以看看Nginx的chunked_filter模块在没有contentlengt的情况下chunk模块可以流式stream的加上长度方便浏览器接收和显示内容。  在响应体过滤模块中尤其要注意的是buf的标志位完整描述可以在“相关结构体”这个节中看到。如果buf中包含last标志 说明是最后一块buf可以直接输出并结束请求了。如果有flush标志说明这块buf需要马上输出不能缓存。如果整块 buffer经过处理完以后没有数据了你可以把buffer的sync标志置上表示只是同步的用处。  当所有的过滤模块都处理完毕时在最后的write_fitler模块中Nginx会将in输出链拷贝到r-out输出链的末尾然后调用sendfile或者writev接口输出。由于Nginx是非阻塞的socket接口写操作并不一定会成功可能会有部分数据还残存在r-out。在下次的调用中Nginx会继续尝试发送直至成功。  
发出子请求 
Nginx过滤模块一大特色就是可以发出子请求也就是在过滤响应内容的时候你可以发送新的请求Nginx会根据你调用的 先后顺序将多个回复的内容拼接成正常的响应主体。一个简单的例子可以参考addition模块。Nginx是如何保证父请求和子请求的顺序呢 当Nginx发出子请求时就会调用ngx_http_subrequest函数将子请求插入父请求的r-postponed链表中。子请求会在主请求执行完毕时获得依次调用。子请求同样会有一个请求所有的生存期和处理过程也会进入过滤模块流程。关键点是在postpone_filter模块中它会拼接主请求和子请求的响应内容。r-postponed按次序保存有父请求和子请求它是一个链表如果前面一个请求未完成那后一个请求内容就不会输出。当前一个请求完成时并输出时后一个请求才可输出当所有的子请求都完成时所有的响应内容也就输出完毕了。 
优化措施 
Nginx过滤模块涉及到的结构体主要就是chain和buf非常简单。在日常的过滤模块中这两类结构使用非常频繁Nginx采用类似freelist重复利用的原则将使用完毕的chain或者buf结构体放置到一个固定的空闲链表里以待下次使用。比如在通用内存池结构体中pool-chain变量里面就保存着释放的chain。而一般的buf结构体没有模块间公用的空闲链表池都是保存在各模块的缓存空闲链表池里面。对于buf结构体还有一种busy链表表示该链表中的buf都处于输出状态如果buf输出完毕这些buf就可以释放并重复利用了。