技术头条 - 一个快速在微博传播文章的方式     搜索本站
您现在的位置首页 --> 系统运维 --> Nginx与Gzip请求

Nginx与Gzip请求

浏览:1928次  出处信息

   前些天,移动端的同事跑来问:某些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 "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))
        end
    end

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

    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
        end
    end
    
    local function writer()
        local t = {}
        return function(data, sz)
            if not data then return table.concat(t) end
            t[#t + 1] = ffi.string(data, sz)
        end
    end
    
    local 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())
        end
    end

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

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

       libzlib.so: cannot open shared object file.

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

    local C = ffi.load 'zlib'

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

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

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

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

    测试

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

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

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

    <?php
    
    $url = 'http://url';
    
    $header = implode("\r\n", array(
        'Content-Type: application/x-www-form-urlencoded',
        'Content-Encoding: gzip',
        'Connection: close',
    ));
    
    $content = gzencode(http_build_query(
        '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更快一些。

建议继续学习:

  1. 配置Nginx+uwsgi更方便地部署python应用    (阅读:105405)
  2. 搜狐闪电邮箱的 Nginx/Postfix 使用模式    (阅读:32530)
  3. 解析nginx负载均衡    (阅读:14520)
  4. Nginx模块开发入门    (阅读:9882)
  5. 检查nginx配置,重载配置以及重启的方法    (阅读:9103)
  6. Cacti 添加 Nginx 监控    (阅读:8923)
  7. Nginx+FastCgi+Php 的工作机制    (阅读:8859)
  8. nginx的配置文件    (阅读:8832)
  9. 奇怪的 Nginx 的 upstream timed out 引起响应 502    (阅读:8158)
  10. 解决IE6从Nginx服务器下载图片不Cache的Bug    (阅读:7105)
QQ技术交流群:445447336,欢迎加入!
扫一扫订阅我的微信号:IT技术博客大学习
© 2009 - 2024 by blogread.cn 微博:@IT技术博客大学习

京ICP备15002552号-1