博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Nginx与Gzip请求
阅读量:4027 次
发布时间:2019-05-24

本文共 5367 字,大约阅读时间需要 17 分钟。

https://huoding.com/2013/09/02/283

Nginx与Gzip请求

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

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

  • : Gzip responses.
  • : Serves precompressed versions of static files.
  • : On-the-fly decompressing of gzipped responses.

可惜它们都是和Response相关的Gzip,而我们需要的是和Request相关的Gzip。

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

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

方案

第一个选择是使用:

local zlib = require "zlib"local encoding = ngx.req.get_headers()["Content-Encoding"]if encoding == "gzip" then    local body = ngx.req.get_body_data()    if body then        local stream = zlib.inflate()        ngx.req.set_body_data(stream(body))    endend

第二个选择是通过LuaJIT的库来包装模块,里有一些现成的可供参考的的例子,不过例子里介绍的是Deflate,而不是Gzip,自己用FFI封装Gzip的话又有点小复杂,好在别人已经做了相关的工作,那就是:

local ffi  = require "ffi"local zlib = require "zlib"local 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 table.concat(t) end        t[#t + 1] = ffi.string(data, sz)    endendlocal encoding = ngx.req.get_headers()["Content-Encoding"]if encoding == "gzip" then    local body = ngx.req.get_body_data()    if body then        local write = writer()        zlib.inflate(reader(body), write, nil, "gzip")        ngx.req.set_body_data(write())    endend

如上例子代码源自,乍看上去,代码里的reader和writer可能会令人费解,其实你可以把它们理解成输入输出接口,可以修改成文件,数据库等等形式。

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

libzlib.so: cannot open shared object file.

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

local C = ffi.load 'zlib'

运行时,会自动补全文件名,如果是Windows,则加载zlib.dll文件,如果是Linux,则加载libzlib.so,但实际上在Linux下,ZLIB扩展的名字是libz.so,而非libzlib.so。

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

local Cif ffi.os == "Windows" then    C = ffi.load "zlib"else    C = ffi.load "z"end

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

测试

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

location ~ \.php$ {    access_by_lua_file /path/to/lua/file;    include fastcgi.conf;    fastcgi_pass 127.0.0.1:9000;}

那么lua-zlib和lua-files两种方案效率如何?下面是我用PHP写的测试脚本:

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代码,新版本会好些。

此条目由发表在分类目录,并贴了、标签。将加入收藏夹。

NGINX与GZIP请求》上有13条评论

  1. 说道:

    当使用 FFI 的时候,只有当你的 Lua 代码确实被 JIT 编译才有可能比使用 CFunction 的 Lua 绑定更快,否则在 LuaJIT 的解释器上运行时肯定更慢。你可以使用 LuaJIT 自带的 jit.v 或者 jit.dump 模块检查你的 Lua 代码有哪些能被 JIT 编译,哪些不能。

    当然,由于 LuaJIT 2.0 的 JIT 编译器缺少很多功能,所以使用 LuaJIT 2.0 的时候就不指望了。可以尝试使用最新的 LuaJIT 2.1,并配合使用 ngx_lua 基于 FFI 实现的新接口 lua-resty-core: 

    另外,我们总是可以使用 on-CPU 火焰图分析 CPU 时间在各条代码路径上是如何分配的。

    • 老王
      说道:

      谨记春哥教诲!

  2. 说道:

    这个讲得是移动端发送gzip请求,服务器端解析。如果是反反过来,服务器段发送的数据是gzip压缩的,移动端需要进行相应解压缩数据吗?现在我们的APP接口数据是json格式的

  3. 说道:

    我比较好奇,直接用php去gzdecode真会影响效率么?理论上request过的的参数一般都比较小。

  4. webwlsong
    说道:

    王哥 我测试的显示用了lua-zlib CPU耗时更长,有时候还会出现长时间的延迟。

    • 法拉利
      说道:

      长时间的延迟?然后CPU一直在等待?

  5. xinster
    说道:

    你好, 我想请教一下 ,怎么安装lua_zlib , 为什么我在make linux的时候会出现以下问题呢 ?

    make[1]: Entering directory `/opt/script/lua-zlib-0.1′
    gcc -O -shared -fPIC -L/usr/lib -L/usr/lib lua_zlib.o -lz -llua -lm -o zlib.so
    /usr/bin/ld: skipping incompatible /usr/lib/libz.so when searching for -lz
    /usr/bin/ld: skipping incompatible /usr/lib/libz.a when searching for -lz
    /usr/bin/ld: skipping incompatible /usr/lib/libz.so when searching for -lz
    /usr/bin/ld: skipping incompatible /usr/lib/libz.a when searching for -lz
    /usr/bin/ld: /usr/local/lib/liblua.a(lapi.o): relocation R_X86_64_32 against `luaO_nilobject_’ can not be used when making a shared object; recompile with -fPIC
    /usr/local/lib/liblua.a: could not read symbols: Bad value
    collect2: ld returned 1 exit status
    make[1]: *** [zlib.so] Error 1
    make[1]: Leaving directory `/opt/script/lua-zlib-0.1′
    make: *** [linux] Error 2

    • hhy
      说道:

      同问,现在您解决了吗?

    • 说道:

      这个是因为你nginx版本太高的缘故,你nginx版本是多少?我记得只支持到1.7以下

  6. narsi
    说道:

    Hello,麻烦请问 Nginx 的 ngx_http_gunzip_module 模块是不是就能处理相同的事情了?

  7. ZERO
    说道:

    写的很好,我是一个wordpress爱好者,想问一下博主,如果我使用php(一个缓存插件)生成了静态的压缩文件(xxx.tar.gz),那么nginx的gzip功能是不是就可以关闭了,而且在访问的时候会更加节省CPU(省去了每次gzip),期待回复。。

  8. passenger
    说道:

    Hello, replace_filter無法處理gzip過的php output, 是否有辦法在nginx中即時gunzip php的output呢?

  9. 天行者
    说道:

    想请教下作者一个问题,下面这段代码中,

    local zlib = require(“zlib”)

    — web服务器支持的返回内容压缩编码类型

    — gzip = gzip头(10字节) + deflate编码的实际内容 + gzip尾(8字节)
    local encoding = ngx.req.get_headers()[“Content-Encoding”]

    if encoding == “gzip” then

    local body = ngx.req.get_body_data()

    if body then
    local stream = zlib.inflate()
    local r = stream(body);
    ngx.req.set_body_data(r);
    local jsonObject = json_decode(r)

    else

    local answer = {}
    answer.result = “100000”
    answer.desc = “请求参数不存在”
    ngx.print(json.encode(answer))
    return
    end
    else
    ngx.print(“body未经过gzip压缩”)
    ngx.exit(ngx.HTTP_OK)
    return
    end
    ================
    那个jsonObject返回的是个nil值,还望作者指点一番。卡在这地方要久了。谢谢

转载地址:http://rblbi.baihongyu.com/

你可能感兴趣的文章
js获取url链接携带的参数值
查看>>
gdb 调试core dump
查看>>
gdb debug tips
查看>>
arm linux 生成火焰图
查看>>
jtag dump内存数据
查看>>
linux和windows内存布局验证
查看>>
linux config
查看>>
linux insmod error -1 required key invalid
查看>>
linux kconfig配置
查看>>
linux不同模块completion通信
查看>>
linux printf获得时间戳
查看>>
C语言位扩展
查看>>
linux dump_backtrace
查看>>
linux irqdebug
查看>>
git 常用命令
查看>>
linux位操作API
查看>>
snprintf 函数用法
查看>>
uboot.lds文件分析
查看>>
uboot start.s文件分析
查看>>
没有路由器的情况下,开发板,虚拟机Ubuntu,win10主机,三者也可以ping通
查看>>