🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
在CONTENT阶段产生的数据被发往客户端(系统发送缓存区)之前,会先经过过滤。Nginx的filter的工作方式和做鱼有些类似。比如一条鱼,可以把它切成鱼片(也可以切块,切泥),然后通过不同的烹饪方法就得到水煮鱼或者日式生鱼片或者废了等等。同样是一条鱼,加工得到的结果却截然不同,就是因为中间不同的工序赋予了这条鱼各种属性。Nginx的filter也是一个道理,前面的Handler好比这条鱼,filter负责加工,最后得到的HTTP响应就会各种各样,格式可以是JSON或者YAML,内容可能多一些或者少一些,HTTP属性可各异,可以选择压缩,甚至内容可以被丢弃。 对应HTTP请求的响应头和响应体,Nginx分别设置了header filter和body filter。两种机制都是采用链表的方式,不同过滤模块对应链表的一个节点,一般而言一个模块会同时注册header filter和body filter。一个典型的filter模块,比如gzip模块使用类似如下的代码来注册: [](http:// "点击提交Issue,反馈你的意见...") static ngx_http_output_header_filter_pt ngx_http_next_header_filter; static ngx_http_output_body_filter_pt ngx_http_next_body_filter; ... static ngx_int_t ngx_http_gzip_filter_init(ngx_conf_t *cf) { ngx_http_next_header_filter = ngx_http_top_header_filter; ngx_http_top_header_filter = ngx_http_gzip_header_filter; ngx_http_next_body_filter = ngx_http_top_body_filter; ngx_http_top_body_filter = ngx_http_gzip_body_filter; return NGX_OK; } 上面的代码中,gzip模块首先在模块的开头声明了两个static类型的全局变量ngx_http_next_header_filter和ngx_http_next_body_filter,在ngx_http_gzip_filter_init函数中,这二个变量分别被赋值为ngx_http_top_header_filter及ngx_http_top_body_filter。而后二者定义在ngx_http.c,并在ngx_http.h头文件中被导出。ngx_http_top_header_filter和ngx_http_top_body_filter实际上是filter链表的头结点,每次注册一个新的filter模块时,它们的值先被保存在新模块的内部全局变量ngx_http_next_header_filter及ngx_http_next_body_filter,然后被赋值为新模块注册的filter函数,而且Nginx filter是先从头节点开始执行,所以越晚注册的模块越早执行。 采用默认编译选项,Nginx默认编译的模块如下: [](http:// "点击提交Issue,反馈你的意见...") ngx_module_t *ngx_modules[] = { &ngx_core_module, &ngx_errlog_module, &ngx_conf_module, &ngx_events_module, &ngx_event_core_module, &ngx_epoll_module, &ngx_regex_module, &ngx_http_module, &ngx_http_core_module, &ngx_http_log_module, &ngx_http_upstream_module, &ngx_http_static_module, &ngx_http_autoindex_module, &ngx_http_index_module, &ngx_http_auth_basic_module, &ngx_http_access_module, &ngx_http_limit_conn_module, &ngx_http_limit_req_module, &ngx_http_geo_module, &ngx_http_map_module, &ngx_http_split_clients_module, &ngx_http_referer_module, &ngx_http_rewrite_module, &ngx_http_proxy_module, &ngx_http_fastcgi_module, &ngx_http_uwsgi_module, &ngx_http_scgi_module, &ngx_http_memcached_module, &ngx_http_empty_gif_module, &ngx_http_browser_module, &ngx_http_upstream_ip_hash_module, &ngx_http_upstream_keepalive_module, &ngx_http_write_filter_module, /* 最后一个body filter,负责往外发送数据 */ &ngx_http_header_filter_module, /* 最后一个header filter,负责在内存中拼接出完整的http响应头, 并调用ngx_http_write_filter发送 */ &ngx_http_chunked_filter_module, /* 对响应头中没有content_length头的请求,强制短连接(低于http 1.1) 或采用chunked编码(http 1.1) */ &ngx_http_range_header_filter_module, /* header filter,负责处理range头 */ &ngx_http_gzip_filter_module, /* 支持流式的数据压缩 */ &ngx_http_postpone_filter_module, /* body filter,负责处理子请求和主请求数据的输出顺序 */ &ngx_http_ssi_filter_module, /* 支持过滤SSI请求,采用发起子请求的方式,去获取include进来的文件 */ &ngx_http_charset_filter_module, /* 支持添加charset,也支持将内容从一种字符集转换到另外一种字符集 */ &ngx_http_userid_filter_module, /* 支持添加统计用的识别用户的cookie */ &ngx_http_headers_filter_module, /* 支持设置expire和Cache-control头,支持添加任意名称的头 */ &ngx_http_copy_filter_module, /* 根据需求重新复制输出链表中的某些节点 (比如将in_file的节点从文件读出并复制到新的节点),并交给后续filter 进行处理 */ &ngx_http_range_body_filter_module, /* body filter,支持range功能,如果请求包含range请求, 那就只发送range请求的一段内容 */ &ngx_http_not_modified_filter_module, /* 如果请求的if-modified-since等于回复的last-modified值, 说明回复没有变化,清空所有回复的内容,返回304 */ NULL }; 从模块的命名可以很容易看出哪些模块是filter模块,一般而言Nginx的filter模块名以filter_module结尾,普通的模块名以module结尾。上面的列表从下往上看,ngx_http_not_modified_filter_module实际上filter链的第一个节点,而ngx_http_write_filter_module是最后一个节点。filter模块的执行顺序特别重要,比如数据经过gzip模块后就变成了压缩之后的数据,如果在gzip模块后面运行的filter模块需要再查看数据的原始内容就不可能了(除非再做解压),第三方模块会被Nginx注册在ngx_http_copy_filter_module之后,ngx_http_headers_filter_module之前。这样设定的原因是为了确保一些模块比如gzip filter,chunked filter,copy filter运行在filter链的开头或尾部。