nginx + fastCGI + Django - obteniendo corrupción de datos en las respuestas enviadas al cliente

10

Estoy ejecutando Django detrás de nginx usando FastCGI. He descubierto que en algunas de las respuestas enviadas al cliente, se está produciendo corrupción aleatoria de datos en el medio de las respuestas (podría ser un par de cientos de bytes en el medio).

En este punto, lo he reducido a ser un error en el controlador FastCGI de nginx o en el controlador FastCGI de Django (es decir, probablemente un error en flup), ya que este problema nunca ocurre cuando ejecuto el servidor Django en modo independiente (es decir runserver). Solo sucede en modo FastCGI.

Otras tendencias interesantes:

  • Tiende a suceder en respuestas más grandes. Cuando un cliente inicia sesión por primera vez, se le envían un montón de fragmentos de 1 MB para sincronizarlos con la base de datos del servidor. Después de esa primera sincronización, las respuestas son mucho más pequeñas (generalmente unos pocos KB a la vez). La corrupción siempre parece suceder en esos fragmentos de 1 MB enviados al inicio.

  • Ocurre más a menudo cuando el cliente está conectado al servidor a través de LAN (es decir, conexión de baja latencia y gran ancho de banda). Esto me hace pensar que hay algún tipo de condición de carrera en nginx o flup que se ve exacerbada por una mayor velocidad de datos.

En este momento, he tenido que solucionar esto poniendo un resumen SHA1 adicional en el encabezado de respuesta y haciendo que el cliente rechace las respuestas donde el encabezado no coincide con la suma de comprobación del cuerpo, pero esta es una solución horrible.

¿Alguien más ha experimentado algo como esto, o tiene alguna sugerencia sobre cómo identificar si es flup o nginx el que tiene la culpa aquí para que pueda presentar un error con el equipo apropiado?

Gracias de antemano por cualquier ayuda.

Nota: También publiqué un error similar en lighttpd + FastCGI + Django hace un tiempo aquí: /programming/3714489/lighttpd-fastcgi-django-truncated-response-sent-to-client-due-to inesperado ... a pesar de que esto no es lo mismo (truncamiento frente a corrupción), está empezando a parecer que el culpable común es flup / Django en lugar del servidor web ...

Editar: también debería tener en cuenta cuál es mi entorno:

  • OSX 10.6.6 en una Mac Mini

  • Python 2.6.1 (Sistema)

  • Django 1.3 (del tarball oficial)

  • flup 1.0.2 (desde Python egg en el sitio flup)

  • nginx + ssl 1.0.0 (de Macports)

EDITAR: en respuesta al comentario de Jerzyk, la ruta de código que ensambla la respuesta se ve (editada para ser sucinta):

# 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

No creo que sea posible que Content-Length sea incorrecto en función de eso, y AFAIK no hay forma de marcar un objeto Django HttpResponse como explícitamente binario en lugar de texto. Además, dado que el problema ocurre solo de manera intermitente, no creo que eso lo explique, de lo contrario, presumiblemente lo vería en cada solicitud.

EDITAR @ionelmc: debe configurar Content-Length en Django; nginx no lo configura para usted, según el ejemplo a continuación, una vez que desactivé la configuración de Content-Length explícitamente:

$ 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
glenc
fuente
Si los fragmentos iniciales no cambian con frecuencia o no son específicos del usuario, ¿tal vez escribir en el disco y servir directamente a través de nginx es una mejor manera?
sunn0
Desafortunadamente, los fragmentos son específicos del usuario y cambian con frecuencia, por lo que ningún almacenamiento en caché de ese tipo sería apropiado para esta aplicación. También estoy interesado en descubrir qué está causando realmente esta corrupción de datos en lugar de solo solucionarlo (lo que ya estoy haciendo con el resumen SHA1 adicional en el encabezado).
glenc 23/10/10
Puedo pensar en dos posibles razones: codificación incorrecta - HttpRespose como texto vs. encabezados binarios o incorrectos (especialmente la longitud del contenido)
Jerzyk
1
@glenc, ¿cuál es un tipo de contenido para esta respuesta? si esto es binario, ¿puedes intentar configurarlo? (por ejemplo, mimetype = 'application / x-ms-excel' o más)
Jerzyk
2
No necesita establecer la longitud del contenido si su codificación de transferencia está fragmentada. El rfc 2616 prohíbe explícitamente esto: "El campo de encabezado Content-Length NO DEBE enviarse si estas dos longitudes son diferentes (es decir, si está presente un campo de encabezado Transfer-Encoding)".
ionelmc

Respuestas:

1

¿Tiene algún tipo de directiva nginx caching (bypass / no_cache) activa para las respuestas fastcgi?

En nginx '1.0.3 Changenotes arreglaron una respuesta de corrupción:

Corrección de errores: una respuesta en caché puede romperse si los valores de la directiva "proxy / fastcgi / scgi / uwsgi_cache_bypass" y "proxy / fastcgi / scgi / uwsgi_no_cache" son diferentes; El error había aparecido en 0.8.46.

Fuente: http://nginx.org/en/CHANGES (sección 1.0.3.)

Michel Feldheim
fuente
0

Quizás la corrupción ocasional solo ocurre si la salida contiene al menos un carácter UTF-8.

La longitud del contenido y la longitud de la cadena no son lo mismo, porque un carácter UTF-8 puede contener de 2 a 5 bytes.

Andy Lee Robinson
fuente
Hmmmm ... si bien esto es cierto, no parece ser la causa porque la corrupción estaba ocurriendo en el medio de los fragmentos de datos y no era simplemente un caso de datos faltantes al final.
glenc
0

Una forma de solucionar este caso un poco más sería:

  • tiene nginx y django ejecutándose en hardware diferente (para que pueda capturar fácilmente el tráfico)
  • capturar el tráfico del cliente a - / -> nginx y nginx - / -> django (es decir, utilizar wireshark)

Una vez que detecte un error en el lado del cliente (basado en el sha1), vaya a la captura de red, mire en la secuencia grabada (TCP) e intente averiguar si el problema es generado por nginx o proviene (directamente) de django .

cipy
fuente
0

Tuve un problema muy similar que me atormentaba durante el tiempo que tuve esta configuración. Al igual que usted, utilizo FastCGI, Nginx y macOS, y encontré corrupción aleatoria en medio de solicitudes grandes (era aproximadamente el 2% de las solicitudes de un documento de 1.5 MB).

Pude resolver mi problema al cambiar a sockets Unix sobre TCP para la conexión FastCGI entre PHP-FPM (en mi caso) y Nginx. No sé qué pieza del rompecabezas es responsable de la corrupción, pero evitar la conexión TCP interna lo solucionó.

Robert
fuente