【问题标题】:Nginx + Tornado ( + curl): Inflate gzipped POST requestNginx + Tornado (+ curl):膨胀压缩的 POST 请求
【发布时间】:2014-01-08 21:41:16
【问题描述】:

我已经设置了一个运行 Tornado(2.4.1 版)并由 Nginx(1.4.4 版)代理的服务器(嗯......两台服务器,但我认为这与这个问题不太相关)。

我需要通过POST 请求定期将json(基本上是文本)文件上传到其中一个文件。这些文件将极大地受益于 gzip 压缩(当我手动压缩文件时,我得到了 90% 的压缩率),但我不知道如何以一种很好的方式膨胀它们。

理想情况下,Nginx 会膨胀它并将其干净利落地传递给 Tornado ......但这不是现在发生的事情,正如你可能已经猜到的那样,否则我不会问这个问题 :-)

这些是我的nginx.conf 文件的相关部分(或我认为相关的部分,因为我对 Nginx 和 Tornado 还很陌生):

user  borrajax;
worker_processes  1;

pid    /tmp/nginx.pid;

events {
    worker_connections  1024;
}


http {
    include       mime.types;
    default_type  application/octet-stream;
    access_log  /tmp/access.log  main;
    error_log   /tmp/error.log;

    # Basic Settings

    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;    
    gzip  on;
    gzip_disable "msie6";
    gzip_types        application/json text/plain text/css application/x-javascript text/xml application/xml application/xml+rss text/javascript image/x-icon image/bmp;
    gzip_http_version 1.1;
    gzip_proxied expired no-cache no-store private auth;

    upstream web {
    server 127.0.0.1:8000;
    }

    upstream input {
        server 127.0.0.1:8200;
    }

    server {
        listen       80 default_server;
        server_name  localhost;
        location / {
            proxy_set_header Host $http_host;
            proxy_set_header X-Real-IP $remote_addr;

            proxy_pass http://web;
        }
    }

    server {
        listen 81 default_server;
        server_name input.localhost;

        location / {
            proxy_set_header Host $http_host;
            proxy_set_header X-Real-IP $remote_addr;

            proxy_pass http://input;
        } 
    }
}

正如我之前提到的,有两个 Tornado 服务器。 main 运行在localhost:8000 上,用于网页和类似的东西。在localhost:8200 上运行的那个是用来接收那些json 文件的那个)这个设置工作正常,除了Gzip 部分。

我希望 Nginx 对到达 localhost:81 的 gzip 请求进行膨胀,并将它们转发到我在 localhost:8200 上运行的 Tornado(膨胀)

这样配置,数据到达Tornado,但是body还是被压缩了,Tornado抛出异常:

[E 140108 15:33:42 input:1085] Uncaught exception POST 
  /input/log?ts=1389213222 (127.0.0.1)
  HTTPRequest(
      protocol='http', host='192.168.0.140:81', 
      method='POST', uri='/input/log?&ts=1389213222', 
      version='HTTP/1.0', remote_ip='127.0.0.1', body='\x1f\x8b\x08\x00\x00', 
      headers={'Content-Length': '1325', 'Accept-Encoding': 'deflate, gzip', 
      'Content-Encoding': 'gzip', 'Host': '192.168.0.140:81', 'Accept': '*/*', 
      'User-Agent': 'curl/7.23.1 libcurl/7.23.1 OpenSSL/1.0.1c zlib/1.2.7', 
      'Connection': 'close', 'X-Real-Ip': '192.168.0.94', 
      'Content-Type': 'application/json'}
   )

我知道我总是可以在 post() Tornado 处理程序中获取请求的 body 并手动对其进行充气,但这听起来……很脏。

最后,这是我用来上传 gzip 文件的 curl 调用:

curl --max-time 60 --silent --location --insecure \
    --write-out "%{http_code}" --request POST \
    --compressed \
    --header "Content-Encoding:gzip" \
    --header "Content-Type:application/json" \
    --data-binary "$log_file_path.gz" \
    "/input/log?ts=1389216192" \
    --output /dev/null \
    --trace-ascii "/tmp/curl_trace.log" \
    --connect-timeout 30

$log_file_path.gz 中的文件是使用gzip $log_file_path 生成的(我的意思是……是一个普通的 Gzip 压缩文件)

这是可行的吗?这听起来应该很简单,但不是......

如果这是通过 Nginx 无法实现的,那么 Tornado 中的自动化方法也可以工作(更可靠和优雅让我在 POST 请求的处理程序中间解压缩文件)喜欢。 .. 像 Django 中间件或类似的东西?

提前谢谢你!!

【问题讨论】:

    标签: file-upload curl nginx tornado http-compression


    【解决方案1】:

    您已经在某处调用json.loads()(Tornado 不会为您解码 json,因此您看到的异常(但未引用)必须来自您自己的代码);为什么不用检查Content-EncodingContent-Type 标头并适当解码的方法替换它?

    【讨论】:

    • 感谢您的回答,但如果我在 curl 调用中删除 Content-Type 标头,它会继续发生。即使是这样,我不应该在例外中看到一个膨胀的身体吗?
    • 我没有说更改请求;我说要更改您的代码以解压缩您收到的数据。由于json解码已经在你的代码而不是框架中(如果你从tornado而不是你自己的代码中得到异常,请发布回溯),将解压放在一些神奇的“中间件”中没有任何好处。跨度>
    • 那么,您建议在 Tornado 处理程序中获取 request.body 并使用我自己的代码对其进行扩充?我有点想避免这种情况,并让 Nginx(或 Tornado)以透明的方式膨胀请求。
    • 是的。你已经在你的代码中调用了 json.loads(request.body) ,对吧?
    • 我是,我是……我想做的是让 request.body 自动解压缩。问题不是json,问题是body在到达json时仍然被压缩
    【解决方案2】:

    我放弃了尝试让 Nginx 或 Tornado 自动扩展 POST 请求的正文,所以我选择了 Ben Darnell 在他的回答中提到的内容。我使用 gzip 压缩文件并将其作为表单的一部分发布(就像我在上传文件一样)。

    我将发布一些处理它的代码,以防万一这对其他人有帮助:

    在客户端(使用 curl 的 bash 脚本):

    要发送的文件的路径(绝对)在变量f 中。变量TMP_DIR 指向/tmp/SCRIPT_NAME 包含尝试执行上传的bash 脚本的名称(即uploader.sh

    zip_f_path="$TMP_DIR/$(basename ${f}).gz"
    [[ -f "${zip_f_path}" ]] && rm -f "${zip_f_path}" &>/dev/null
    gzip -c "$f" 1> "${zip_f_path}"
    if [ $? -eq 0 ] && [[ -s "${zip_f_path}" ]]
    then
        response=$(curl --max-time 60 --silent --location --insecure \
                        --write-out "%{http_code}" --request POST \
                        "${url}" \
                        --output /dev/null \
                        --trace-ascii "${TMP_DIR}/${SCRIPT_NAME}_trace.log" \
                        --connect-timeout 30 \
                        --form "data=@${zip_f_path};type=application/x-gzip")              
    else
        echo "Attempt to compress $f into $zip_f_path failed"
    fi
    

    在服务器中(在 Tornado 处理程序中):

    try:
        content_type = self.request.files['data'][0]['content_type']
        if content_type == 'application/x-gzip':
            gzip_decompressor = GzipDecompressor()
            file_body = gzip_decompressor.decompress(
                            self.request.files['data'][0]['body'])
            file_body += gzip_decompressor.flush()
        else:
            file_body = self.request.files['data'][0]['body']
    except:
        self.send_error(400)
        logging.error('Failed to interpret data: %s',
                      self.request.files['data'])
        return
    

    【讨论】:

      猜你喜欢
      • 2014-07-25
      • 2017-12-12
      • 1970-01-01
      • 1970-01-01
      • 2011-06-19
      • 1970-01-01
      • 2011-02-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多