遂宁信息港

当前位置:

Nginx与Gzip请求

2019/10/13 来源:遂宁信息港

导读

前些天,移动端的同事跑来问:某些API需要传输大数据,Nginx服务器能否支持Gzip请求?一方面可以节省移动端流量;另一方面还可以加快传输

  前些天,移动端的同事跑来问:某些API需要传输大数据,Nginx服务器能否支持Gzip请求?一方面可以节省移动端流量;另一方面还可以加快传输速度,提升用户体验。对于Apache来说,利用SetInputFilter,可以很轻松的实现这个功能,那么Nginx如何做呢?

  既然移动端发送的是Gzip请求,自然需要想想如何在服务端解压缩。搜索一下现成的Nginx的模块,发现和Gzip相关的模块有如下几个:

  Gzip:Gzip responses. Gzip Precompression:Serves precompressed versions of static files. Gunzip:On-the-fly decompressing of gzipped responses.可惜它们都是和Response相关的Gzip,而我们需要的是和Request相关的Gzip。

  在我们的实际情况里,很多接口都是用PHP做的,于是自然想到用PHP的gzdecode方法来解压缩Gzip请求,不过终出于效率的担心放弃了。

  每当我遇到难题的时候就会想起lua-nginx-module,它总是能屡建奇功,这次自然也不例外,仔细搜索了一下OpenResty社区,发现有人遇到了同样的问题,春哥在讨论中给出了建议,不过并没有涉及具体的实现逻辑,于是我查了资料总结了一下。

  方案个选择是使用lua-zlib:

  local zlib = require zliblocal encoding = t_headers()[Content-Encoding]if encoding == gzip then local body = t_body_data() if body then local stream = flate() t_body_data(stream(body)) endend第二个选择是通过LuaJIT的FFI库来包装ZLIB模块,官方教程里有一些现成的可供参考的的例子,不过例子里介绍的是Deflate,而不是Gzip,自己用FFI封装Gzip的话又有点小复杂,好在别人已经做了相关的工作,那就是lua-files:

  local ffi = require ffilocal zlib = require zliblocal function reader(s) local done return function() if done then return end done = true return s endendlocal function writer() local t = {} return function(data, sz) if not data then return ncat(t) end t[#t + 1] = ring(data, sz) endendlocal encoding = t_headers()[Content-Encoding]if encoding == gzip then local body = t_body_data() if body then local write = writer() flate(reader(body), write, nil, gzip) t_body_data(write()) endend如上例子代码源自zlib_a,乍看上去,代码里的reader和writer可能会令人费解,其实你可以把它们理解成输入输出接口,可以修改成文件,数据库等等形式。

  别高兴太早,当你运行时,很可能会遇到如下错误:

  : cannot open shared object file.

  实际上这是因为如下a代码的缘故:

  local C = ad 'zlib'运行时,ad会自动补全文件名,如果是Windows,则加载l文件,如果是Linux,则加载,但实际上在Linux下,ZLIB扩展的名字是,而非。

  知道的问题的原委,我们自然就知道如何修改代码了:

  local Cif == Windows then C = ad zlibelse C = ad zend有时候我们不推荐直接修改第三方库的代码,因为这样的话,每次第三库更新代码,我们都要做对应的修改,一旦忘记就会出错,这时候可以考虑做一个软连接别名。

  测试开篇说过,接口都是用PHP做的,不过请求里的Gzip数据是用LUA处理的,如何让PHP使用LUA处理后的数据呢?不同的语言似乎是个难题,好在Nginx有Phases一说,PHP作为FastCGI模块工作在content阶段,LUA可以工作在access阶段,这样它们就和谐了:

  location ~ .php$ { access_by_lua_file /path/to/lua/file; include nf; fastcgi_pass 127.0.0.1:9000;}那么lua-zlib和lua-files两种方案效率如何?下面是我用PHP写的测试脚本:

  ?php$url = 'http://url';$header = implode(rn, array( 'Content-Type: application/x-www-form-urlencoded', 'Content-Encoding: gzip', 'Connection: close',));$content = gzencode(http_build_query(array( 'foo' = str_repeat('x', 100), 'bar' = str_repeat('y', 100),)));$options = array( 'http' = array( 'protocol_version' = '1.1', 'method' = 'POST', 'header' = $header, 'content' = $content, ),);$context = stream_context_create($options);for ($i = 0; $i 1000; $i++) { file_get_contents($url, false, $context);}?很多人写测试脚本的时候,喜欢在开始结束部分加上时间,这样相减就得到了代码实际运行的时间,其实这是不必要的,利用Linux自带的time就可以获取运行时间:

  shell time php /path/to/php/file按春哥说的,理论上FFI应该更高效,不过从我的测试结果看,lua-zlib比lua-files更快一些,这是因为目前的FFI还不能完整编译LUA代码,新版本会好些。

健康
民生教育
资讯
标签

友情链接