zoukankan      html  css  js  c++  java
  • nginx+lua 记一次后端程序压缩数据后导致前端乱码问题

    前言

    大家都知道压缩包如果不解压直接查看的话,都是一堆乱码,目前我们使用nginx+lua 对用户请求进行代理时( 客户端-->ngx+lua-->后端服务器 ),发现有些请求返回的数据都是乱码,这就乱码的原因就是直接查看未解压的压缩包

    问题表现

    我们在上线候测试时,发现浏览器某个按钮不能用了,通过F12查看发现这个请求返回内容都是乱码,如下所示:

    我又上日志上查看,发现nginx记录的日志也是乱码,如下所示:

    排查过程

    我第一感觉认为是后端服务器返回了乱码数据给我,所以才是乱码的,呵呵。于是找负责人去了,人家说没有动呀。于是我们只能从自己身上找问题。我想了以下可能会出问题的点:

    1. 后端数据本身有问题(已被排除)
    2. 后端返回数据太大,nginx+lua只收了一些数据,数据没收全导致。
    3. 后端的数据被压缩了导致乱码

    第一点毫无疑问排除后,就准备验证第二点,我通过tcpdump 在服务端(后端服务器)和nginx+lua上抓包,抓包能够分析的更全面所以优先考虑它,然后下载PC机用wireshark 分析,由于是HTTPS传输的,都是加密的内容,wireshark 啥也看不到,如下所示:

    我参考网上很多文章尝试去解开这个HTTPS数据流,结果都搞不定,花了半天时间,就放弃了。那怎么去验证?换条路,nginx+lua里面使用的 lua-resty-http(github地址) 模块去构造HTTP请求,那可以很方便查看接受到的数据长度,在日志里面打印出接受的数据长度即可,然后查看后端服务器的nginx日志,看看nginx发送的数据长度有多少。

    lua记录接收到的数据长度:

                res,err = httpc:request_uri(url, {
                    method = method,
                    body = body,
                    headers = headers,
                    keepalive_timeout = 60000,  -- ms
                    keepalive_pool = 40,
                })
    
                local body = res.body
                log(ERR,"body length: ",#body)
    

    我通过对比发现,nginx+lua收到的数据竟然是510字节,而后端服务器的nginx日志显示发送了1300字节,这个怎么回事呢? 难道真的是因为数据没有收全导致乱码? 带着这些问题,我继续深挖。

    我首先翻开lua-resty-http的源代码,基本上把request_uri这个方法的仔细看了一遍,然后在里面各个条件分支上打了日志,发现乱码的请求,都会走到这个条件分支:

            if version == 1.1 and str_find(encoding, "chunked", 1, true) ~= nil then
                body_reader, err = _chunked_body_reader(sock)
    

    这大概找到了前进的方向了,chunked 这个关键字。然后继续往下走,在 _chunked_body_reader 发现一个有意思的:

    length = tonumber(str, 16)
    

    我一开始没有想明白这个怎么回事(原谅鄙人才疏学浅),经过查阅相关资料才发现,chunked传输都是16进制的,所以这个长度在这里转了下,需要转为16进制。

    ,于是继续往前走

    我对比了不乱码的请求和乱码的请求,发现请求的头信息,接口地址都一样,唯一不同的就是GQL语句不一样,我又对比返回来的头信息,发现乱码的头信息多了2字段,他们是

    1. Transfer-Encoding : chunked
    2. Content-Encoding : gzip

    第一个chunked表示数据太长需要进行分片(个人理解,应该没有理解歪曲),第二个gzip表示内容编码 格式为 gzip,其实还有点被我忽略了,前端请求时的头信息有这一段:Accept-Encoding: gzip ,这表明可以返回的数据接受gzip的编码。所以服务器端就进行压缩了。

    了解了以上几点,我此时大概可以推断出来。 ngx+lua 接受的数据是510,数据不全的原因那么就有4种情况:

    1. 数据内容太长被切片传输了(这个应该被排除,因为Content-Encoding : gzip表明已被压缩了)。
    2. 数据内容被压缩了
    3. 数据内容太长,切片传输前被压缩了。
    4. 数据内容太长,压缩后再进行切片传输。

    到此,只能是一一排除了。我最开始去验证第二个情况,我把接收到的乱码内容写到磁盘上,再拿到PC机的360解压,发现解压后的内容就是json串。
    写入磁盘代码如下:

    local f = io.open("/tmp/luanma.zip","w")
    f:write(resp.body)
    f:close()
    

    处理方法

    显然,我们一下子就碰对了。找到问题了,这个就是后端服务器返回数据的时候,由于数据稍微长点,所以就进行了压缩,导致nginx+lua 接收到的数据是二进制的,需要再解压下才能还原回json串。于是添加以下代码:

            local ret = res.body
            if res.headers["Transfer-Encoding"] == "chunked" and string.find( res.headers["Content-Encoding"],"gzip" ) ~= nil then
                ret = zlib.inflate()(ret)  -- 这里是压缩文件,需要进行解压处理,否则浏览器收到是乱码
            end
            ngx.print(ret)
    

    至此,添加完这个代码,重启下nginx,问题被解决了。

    zlib 的使用方法可以查看官网: https://github.com/brimworks/lua-zlib/

    安装zlib

    我这里使用apt安装,源码安装比较费劲,所以推荐使用apt-get或yum 。ubuntu系统如下

    apt-get install lua-zlib lua-zlib-dev
    cp /usr/lib/x86_64-linux-gnu/lua/5.1/zlib.so  /usr/local/lib/lua/5.1/ # 一定要复制过去,不然引入zlib会提示找不到。
    

    杂谈

    问题1

    为什么会被压缩,我个人认为是 nodejs 发请求时明确了可以接受gzip压缩内容,http头信息里面 Accept-Encoding: gzip 表明了这点,所以后端返回了压缩内容,

    问题2

    奇怪的是,我看所有的请求头信息都有这个字段,但是只有个别请求的内容被压缩了。我个人认为是数据长度太长了。首次发现乱码的请求,返回的数据长度接近2K

  • 相关阅读:
    王妃
    某个奇怪的引理 学习总结
    多项式求ln,求exp,开方,快速幂 学习总结
    第二类斯特林数 学习总结
    cojs QAQ的图论题 题解报告
    cojs QAQ的序列 解题报告
    QAQ OI生涯の最后一个月
    cojs 疯狂的字符串 题解报告
    【51Nod 1238】最小公倍数之和 V3
    【51Nod 1190】最小公倍数之和 V2
  • 原文地址:https://www.cnblogs.com/liaojiafa/p/14349178.html
Copyright © 2011-2022 走看看