Nginx + Tornado(+ curl):膨胀gzipped POST请求

时间:2014-01-08 21:41:16

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

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

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

理想情况下,Nginx会对它进行充气并将它整洁地传递给龙卷风......但这不是现在正在发生的事情,因为你可能已经猜到了,否则我不会问这个问题 :-)

这些是我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,但是主体仍然被压缩,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'}
   )

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

最后,这是我用来上传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中间件或类似的东西?

提前谢谢!!

2 个答案:

答案 0 :(得分:1)

你已经在某个地方调用了json.loads()(Tornado没有为你解码json,所以你看到的异常(但没有引用)必须来自你自己的代码);为什么不直接用检查Content-EncodingContent-Type标题并正确解码的方法替换它?

答案 1 :(得分:0)

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

我会发布一些代码来处理它,以防万一这会帮助其他人:

在客户端(使用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