2014-01-08 4 views
1

У меня есть сервер (ну ... два сервера, но я не думаю, что это слишком актуально для этого вопроса) работает Tornado (версия 2.4.1) и проксимируется Nginx (версия 1.4.4).Nginx + Tornado (+ curl): Inflate gzip POST-запрос

Мне нужно периодически загружать файлы json (в основном текстовые) в один из них через запрос POST. Этим файлам будет очень полезно сжатие gzip (я получаю коэффициент сжатия 90%, когда я сжимаю файлы вручную), но я не знаю, как надуть их красиво.

В идеале, Nginx бы надуть его и передать его почистить аккуратный Торнадо ... но это не то, что сейчас происходит, как вы уже, наверное, догадались, в противном случае я бы не стал задавать этот вопрос :-)

Эти соответствующие части моего nginx.conf файла (или части, которые я думаю актуальны, потому что я довольно новыми для Nginx и Торнадо):

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. главный один работает на localhost:8000 для веб-страниц и подобных материалов. Тот, который работает на localhost:8200, предназначен для приема тех файлов json). Эта настройка работает нормально, за исключением части Gzip.

Я хотел бы, чтобы Nginx надуть с gzip'нутыми запросами, которые приходят к localhost:81, и направить их на Tornado я работаю на localhost:8200 (завышено)

С конфигурацией, как это, данные достигает 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-й в обработчике post() Торнадо и надуть его вручную, но это только кажется ... грязное.

Наконец, это 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 сжатого файла)

Это что-то выполнимое? Это звучит как-то, что должно быть довольно прямо вперед, но nopes ...

Если это что-то не выполнимо через Nginx, автоматизированный метод Tornado будет работать тоже (что-то более надежное и элегантный, что с меня разжатия файлы в середине обработчика запроса POST) Как ... что-то вроде Django middlewares или что-то в этом роде?

Благодарим вас заранее!

ответ

1

Вы уже звоните json.loads() (Tornado не расшифровывает json для вас, поэтому исключение, которое вы видите (но не указываете), должно исходить из вашего собственного кода); почему бы просто не заменить это методом, который рассматривает заголовки Content-Encoding и Content-Type и соответственно декодирует?

+0

Спасибо за ваш ответ, но если я удалю заголовок Content-Type в вызове curl, это будет продолжаться. Даже если бы это было так, разве я не должен видеть завышенное тело в исключении? – BorrajaX

+0

Я не сказал изменить запрос; Я сказал, чтобы изменить ваш код, чтобы распаковать данные, которые вы получаете. Поскольку json-декодирование уже находится в вашем коде вместо фреймворка (если вы получаете исключение из торнадо вместо своего собственного кода, отправьте трассировку), нет никакой пользы для декомпрессии в каком-то магическом «промежуточном программном обеспечении». –

+0

Итак, что вы рекомендуете, это получить request.body в обработчике Tornado и раздуть его собственным кодом? Я вроде хотел этого избежать, и Nginx (или Tornado) раздувает запрос прозрачным способом. – BorrajaX

0

Я отказался от попыток, чтобы Nginx или Tornado автоматически расширяли тело запроса POST, поэтому я пошел с тем, что Бен Дарнелл упомянул в своем ответе. Я сжимаю файл с помощью gzip и POST его как часть формы (почти так же, как если бы я загружал файл).

Я собираюсь пост биты кода, которые заботятся о нем, только в случае, если это помогает кто-то еще:

В клиенте (Баш скрипт, использующий завиток):

Путь (абсолютный) в файл для отправки находится в переменной f. Переменная TMP_DIR указывает на /tmp/ и SCRIPT_NAME содержит имя скрипта, Баш пытается выполнить загрузку (а именно 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 "[email protected]${zip_f_path};type=application/x-gzip")    
else 
    echo "Attempt to compress $f into $zip_f_path failed" 
fi 

на сервере (в обработчике Торнадо):

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