【OT】请教个nginx proxy时候后端返回gzip问题

1,330 views
Skip to first unread message

刘太华

unread,
Mar 20, 2012, 2:10:49 AM3/20/12
to open...@googlegroups.com
Hi,
    不知道算违规不,跟ngx_lua没直接关系。假如nginx做proxy时,开启gzip,同时后端web也是开启gzip的,本来我觉得如果同时开启了gzip应该会导致多次压缩,从而客户端浏览器在展现的时候会乱码,这个情况我以前似乎遇到过,不过已经不敢确认了
今天重新抓包看,即使后端返回的是gzip(抓包确认的),并且做proxy的nginx也开启gzip,浏览器展现并没问题。

这样说来,
1、nginx在处理压缩的时候,会看后端response返回的header中是否有Content-Encoding:gzip?然后才来决定是否对此次response做压缩
2、 或者ngx接到后端response,解压后再次做压缩,然后发送给客户端?

如果是1的情况,那使用ngx_lua做一些capture的时候,能碰到获取到的response压缩过的,那么此时用lua对返回的body做逻辑的时候,应该会遇到解压缩的问题?


agentzh

unread,
Mar 20, 2012, 2:29:29 AM3/20/12
to open...@googlegroups.com
On Tue, Mar 20, 2012 at 2:10 PM, 刘太华 <def...@gmail.com> wrote:
> 这样说来,
> 1、nginx在处理压缩的时候,会看后端response返回的header中是否有Content-Encoding:gzip?然后才来决定是否对此次response做压缩

是的。实现细节可以参见 nginx 1.0.12 源码树中 src/http/modules/ngx_http_gzip_filter_module.c 文件的第 250 行。相关的那段代码是

   if (!conf->enable
       || (r->headers_out.status != NGX_HTTP_OK
           && r->headers_out.status != NGX_HTTP_FORBIDDEN
           && r->headers_out.status != NGX_HTTP_NOT_FOUND)
       || (r->headers_out.content_encoding
           && r->headers_out.content_encoding->value.len)
       || (r->headers_out.content_length_n != -1
           && r->headers_out.content_length_n < conf->min_length)
       || ngx_http_test_content_type(r, &conf->types) == NULL
       || r->header_only)
   {
       return ngx_http_next_header_filter(r);
   }

即当 Content-Encoding 响应头已经存在时,ngx_gzip 的响应头过滤器会直接跳过处理,转给下一个过滤器。

> 2、 或者ngx接到后端response,解压后再次做压缩,然后发送给客户端?
>

这样做开销太大,解压和再压缩没有实际意义,只是徒耗 CPU 计算资源 :)


> 如果是1的情况,那使用ngx_lua做一些capture的时候,能碰到获取到的response压缩过的,那么此时用lua对返回的body做逻辑的时候,应该会遇到解压缩的问题?
>

一般,让使用 ngx_proxy 访问后端 http 服务,同时在 ngx_lua 中使用 capture API 时,应该显式地控制是否启用 gzip(因为默认情况下,nginx 子请求会自动继承父请求的请求头). 例如在配置了 proxy_pass 的内部 location 中通过

  proxy_set_header  Accept-Encoding  "";

显式地禁用后端的 gzip 压缩,或者通过

  proxy_set_header  Accept-Encoding  "gzip";

显式地启用后端压缩。看你具体的应用场景了。如果让上游 http 服务自己进行 gzip 压缩的话,在 lua 里得到的数据自然就是 gzip 压缩以后的,所以需要使用 lua 世界里的 gzip 库来解压,例如 lua-zlib 库:https://github.com/brimworks/lua-zlib

在 ngx_lua 模块的 github issue #12 "How to handle gziped capture?" 中对此问题亦有比较完整的讨论:

https://github.com/chaoslawful/lua-nginx-module/issues/12

Best regards,
-agentzh

刘太华

unread,
Mar 20, 2012, 2:50:13 AM3/20/12
to open...@googlegroups.com
多谢agentzh解惑,了解了。




--
刘太华


lyle...@gmail.com

unread,
Oct 23, 2013, 8:10:30 AM10/23/13
to open...@googlegroups.com
在 2012年3月20日星期二UTC+8下午2时29分29秒,agentzh写道:


你好,agentzh

目前我们有一个需求,不知道你有什么好的建议,向你请教一下。

我们的业务场景如下:
使用nginx做反向代理,开启nginx代理缓存
1. nginx在向源站发起请求时,我们希望通过强制增加
Accept-Encoding:gzip 来告知源希望得到返回gzip压缩的数据
2. 如果源开启了gzip,会对我们请求的资源进行压缩,同时在响应头中增加 Content-Encoding:gzip。这样nginx在接收到数据后,在开启缓存的情况下,会在磁盘存储内容是经过gzip压缩后的数据。
如果源站未开启gzip压缩,返回的响应内容应该是未经过gzip压缩的

我们的需求是:
1. 希望nginx永远缓存经过gzip压缩的内容。即便源站不支持gzip压缩,在得到源站的响应数据后,我们实时进行gzip(对于同一个数据,只会压缩一次,比只存储原始内容,应对相同url请求时,每次进行gzip压缩要节省cpu重复执行次数)

2. 由于存储了压缩后的数据,为了应对部分浏览器不支持gzip解压缩,我们使用了gunzip模块,针对这种情况对磁盘存储的压缩过的内容进行实时解压缩(经过我们分析,这样的请求应该占少数)

我们这样设计的目的有两点:
1. 希望充分利用磁盘空间,尽量多的存储文件数
2. 由于大多数客户端请求都是支持gzip解压缩的,我们在处理这些请求时不需要实时gzip压缩数据,希望节省cpu


但是遇到的问题是:

需要在upstream响应上游的body时,检测上游的响应头,是否包含Content-Encoding:gzip,如果不包含,需要实时进行gzip压缩,将内容存储到磁盘文件,同时维护一个变量,需要代表该份内容已经执行过压缩,在请求执行 gzip_filter模块时,除了原有nginx的检查外,还需要检查该变量,以保证不会重复执行格gzip压缩,导致返回给客户的数据时经过两次压缩的数据。


基本的实现思路虽然了解,但是以我们目前的开发水平,还不足以十分有把握的能保证改对每一个地方,关于这块,你有什么好的建议

比如:
1. 我们是必须修改upstream模块来加入上述功能,同时在request结构体中增加一个变量,并在upstream中实际执行过gzip压缩后进行设置

2. 还是,我们可以独立开发出一个模块,提供一些类似于gzip的指令,在配置文件中类似于配置gzip on;的处理一样。但是这样的自定义模块,该如何嵌入到upstream处理上游响应的代码中,是在有点困惑,真心的希望得到你的帮助

谢谢

by lingyun

Reply all
Reply to author
Forward
0 new messages