nginx + fastCGI + Django - получение искажения данных в ответах, отправленных клиенту


10

Я использую Django за nginx, используя FastCGI. Я обнаружил, что в некоторых ответах, отправляемых клиенту, в середине ответов происходит случайное повреждение данных (может быть пара сотен байтов или около того в середине).

На данный момент я сузил это до того, чтобы быть либо ошибкой в ​​обработчике FastCGI в nginx, либо обработчиком FastCGI в Django (то есть, вероятно, ошибкой в ​​flop), поскольку эта проблема никогда не возникает, когда я запускаю сервер Django в автономном (то есть runserver) режиме. Это происходит только в режиме FastCGI.

Другие интересные тенденции:

  • Это имеет тенденцию происходить при больших ответах. Когда клиент входит в систему в первый раз, ему отправляется куча блоков по 1 МБ, чтобы синхронизировать их с БД сервера. После этой первой синхронизации ответы становятся намного меньше (обычно несколько килобайт за раз). Похоже, что коррупция всегда происходит в тех 1-мегабайтных фрагментах, которые отправляются с самого начала

  • Это происходит чаще, когда клиент подключен к серверу через локальную сеть (то есть соединение с низкой задержкой и высокой пропускной способностью). Это заставляет меня думать, что в nginx или flup есть какое-то состояние гонки, которое усугубляется увеличением скорости передачи данных.

Прямо сейчас мне пришлось обойти это, поместив дополнительный дайджест SHA1 в заголовок ответа, и заставить клиента отклонять ответы, когда заголовок не соответствует контрольной сумме тела, но это своего рода ужасное решение.

Кто-нибудь еще сталкивался с чем-то подобным, или есть какие-либо указания относительно того, как определить, является ли это ошибкой flup или nginx, чтобы я мог сообщить об ошибке в соответствующую команду?

Заранее благодарю за любую помощь.

Примечание: я также опубликовал похожую ошибку в lighttpd + FastCGI + Django некоторое время назад здесь: /programming/3714489/lighttpd-fastcgi-django-truncated-response-sent-to-client-due-to - неожиданно ... хотя это не одно и то же (усечение по сравнению с повреждением), все начинает казаться, что распространенным виновником является flup / Django, а не веб-сервер ..

Изменить: я должен также отметить, что моя среда:

  • OSX 10.6.6 на Mac Mini

  • Python 2.6.1 (система)

  • Django 1.3 (из официального архива)

  • Flup 1.0.2 (из яйца Python на сайте Flup)

  • nginx + ssl 1.0.0 (из Macports)

РЕДАКТИРОВАТЬ: В ответ на комментарий Ежика путь кода, который собирает ответ выглядит (отредактировано для краткости):

# This returns an objc NSData object, which is an array.array 
# when pushed through the PyObjC bridge
ret = handler( request ) 

response = HttpResponse( ret )
response[ "Content-Length" ] = len( ret )
return response

Я не думаю, что возможно, что Content-Length является неправильным на основании этого, и AFAIK нет никакого способа пометить объект Django HttpResponse как явно двоичный, в отличие от текста. Кроме того, поскольку проблема возникает только периодически, я не думаю, что это объясняет это иначе, вероятно, вы увидите это при каждом запросе.

РЕДАКТИРОВАТЬ @ionelmc: Вы должны установить Content-Length в Django - nginx не устанавливает это для вас, как показано в примере ниже, как только я явно отключил установку Content-Length:

$ curl -i http://localhost/io/ping
HTTP/1.1 200 OK
Server: nginx/1.0.0
Date: Thu, 23 Jun 2011 13:37:14 GMT
Content-Type: text/html; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive

AKSJDHAKLSJDHKLJAHSD

Если начальные чанки не меняются часто или не зависят от пользователя, может быть, лучше записать на диск и обслуживать напрямую через nginx?
sunn0

К сожалению, чанки являются как пользовательскими, так и часто меняющимися, поэтому такого кеширования для этого приложения не будет. Я также хотел бы выяснить, что на самом деле вызывает это повреждение данных, а не просто обойти его (что я уже делаю с дополнительным дайджестом SHA1 в заголовке).
Гленк

Я могу думать о двух возможных причинах: неправильная кодировка - HttpRespose как текст против двоичных или неправильные заголовки (особенно длина содержимого)
Jerzyk

1
@glenc, что такое тип контента для этого ответа? если это двоичный файл - можете ли вы попытаться установить его? (например, mimetype = 'application / x-ms-excel' или другое)
Ежиц

2
Вам не нужно устанавливать длину содержимого, если ваша Transfer-Encoding разделена на части. rfc 2616 явно запрещает это: «Поле заголовка Content-Length НЕ ДОЛЖНО отправляться, если эти две длины различны (т. е. если присутствует поле заголовка Transfer-Encoding)».
ionelmc

Ответы:


1

Есть ли у вас какая-либо директива кэширования nginx (bypass / no_cache) для ответов fastcgi?

В nginx '1.0.3 Changenotes исправлено повреждение ответа:

Исправлено: кэшированный ответ может быть прерван, если значения директив proxy / fastcgi / scgi / uwsgi_cache_bypass и proxy / fastcgi / scgi / uwsgi_no_cache различны; ошибка появилась в 0.8.46.

Источник: http://nginx.org/en/CHANGES (раздел 1.0.3.)


0

Возможно, случайное повреждение происходит только в том случае, если выходные данные содержат хотя бы один символ UTF-8.

Длина содержимого и длина строки - это не одно и то же, потому что один символ UTF-8 может содержать от 2 до 5 байтов.


Хм-м-м ... хотя это действительно так, вряд ли это является причиной, потому что повреждение происходило в середине фрагментов данных, а не просто как случай отсутствия данных в конце.
Гленк

0

Один из способов устранения неполадок в этом случае - это:

  • nginx и django работают на другом оборудовании (чтобы вы могли легко захватывать трафик)
  • захватить трафик от клиента к - / -> nginx и nginx - / -> django (т.е. использовать wireshark)

Как только вы обнаружите ошибку на стороне клиента (на основе sha1), перейдите к захвату сети, посмотрите на записанный (TCP) поток и попытайтесь выяснить, сгенерирована ли проблема nginx или она возникла (напрямую) из django ,


0

У меня была очень похожая проблема, которая мучала меня до тех пор, пока у меня была эта установка. Как и вы, я использую FastCGI, Nginx и macOS, и обнаружил случайное повреждение в середине больших запросов (это было около 2% запросов от 1,5 МБ документа).

Я смог решить мою проблему, переключившись на сокеты Unix через TCP для соединения FastCGI между PHP-FPM (в моем случае) и Nginx. Я не знаю, какая часть головоломки ответственна за повреждение, но избегание внутреннего соединения TCP все-таки исправило.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.