nginx + fastCGI + Django - ottenere il danneggiamento dei dati nelle risposte inviate al client


10

Sto correndo Django dietro nginx usando FastCGI. Ho scoperto che in alcune delle risposte inviate al client, si sta verificando una corruzione casuale dei dati nel mezzo delle risposte (potrebbero essere circa duecento byte al centro).

A questo punto l'ho ridotto a essere un bug nel gestore FastCGI di nginx o nel gestore FastCGI di Django (cioè probabilmente un bug in flup), poiché questo problema non si verifica mai quando eseguo il server Django in modalità standalone (ie runserver). Succede solo in modalità FastCGI.

Altre tendenze interessanti:

  • Tende ad accadere con risposte più ampie. Quando un client accede per la prima volta, viene inviato un gruppo di blocchi da 1 MB per sincronizzarli con il DB del server. Dopo quella prima sincronizzazione, le risposte sono molto più piccole (di solito pochi KB alla volta). La corruzione sembra sempre accadere su quei blocchi da 1 MB inviati all'inizio.

  • Accade più spesso quando il client è connesso al server tramite LAN (ovvero connessione a bassa latenza e larghezza di banda elevata). Questo mi fa pensare che ci sia una specie di condizione di razza in nginx o flup che è esacerbata da un aumento della velocità dei dati.

In questo momento, ho dovuto aggirare questo problema inserendo un digest SHA1 aggiuntivo nell'intestazione della risposta e facendo in modo che il client respingesse le risposte in cui l'intestazione non corrisponde al checksum del corpo, ma questa è una specie di soluzione orribile.

Qualcun altro ha sperimentato qualcosa del genere, o ha qualche suggerimento su come identificare se è flup o nginx che è in errore qui in modo da poter presentare un bug con il team appropriato?

Grazie in anticipo per qualsiasi aiuto.

Nota: ho anche pubblicato un bug simile in lighttpd + FastCGI + Django qualche tempo fa: /programming/3714489/lighttpd-fastcgi-django-truncated-response-sent-to-client-due-to -inaspettato ... anche se questa non è la stessa cosa (troncamento contro corruzione), sta iniziando a sembrare che il colpevole comune sia flup / Django piuttosto che il web server ..

Modifica: dovrei anche notare qual è il mio ambiente:

  • OSX 10.6.6 su Mac Mini

  • Python 2.6.1 (Sistema)

  • Django 1.3 (dal tarball ufficiale)

  • flup 1.0.2 (dall'uovo di Python sul sito di flup)

  • nginx + ssl 1.0.0 (da Macports)

EDIT: in risposta al commento di Jerzyk, il percorso del codice che assembla la risposta appare come (modificato per succinto):

# 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

Non credo sia possibile che la lunghezza del contenuto sia errata in base a ciò, e AFAIK non ha modo di contrassegnare un oggetto Django HttpResponse in modo esplicito binario rispetto al testo. Inoltre, poiché il problema si verifica solo in modo intermittente, non penso che lo spieghi altrimenti presumibilmente lo vedresti su ogni richiesta.

EDIT @ionelmc: Devi impostare Content-Length in Django - nginx non lo imposta per te, come nell'esempio seguente una volta disabilitato l'impostazione Content-Length esplicitamente:

$ 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

Se i blocchi iniziali non cambiano spesso o non sono specifici dell'utente, forse scrivere su disco e servire direttamente tramite nginx è un modo migliore?
dom0,

Sfortunatamente, i blocchi sono specifici dell'utente e cambiano spesso, quindi nessuna cache di quel tipo sarebbe appropriata per questa applicazione. Sono anche desideroso di scoprire cosa sta effettivamente causando questa corruzione dei dati piuttosto che semplicemente aggirarlo (cosa che sto già facendo con il digest extra SHA1 nell'intestazione).
glenc,

Posso pensare a due possibili ragioni: codifica errata - HttpRespose come testo vs. intestazioni binarie o errate (in particolare la lunghezza del contenuto)
Jerzyk

1
@glenc cos'è un tipo di contenuto per questa risposta? se questo è binario, puoi provare a impostarlo? (es. mimetype = 'application / x-ms-excel' o altro)
Jerzyk

2
Non è necessario impostare la lunghezza del contenuto se la codifica di trasferimento è suddivisa in blocchi. rfc 2616 lo proibisce esplicitamente: "Il campo di intestazione Content-Length NON DEVE essere inviato se queste due lunghezze sono diverse (cioè, se è presente un campo di intestazione Transfer-Encoding)."
ionelmc,

Risposte:


1

Hai qualche tipo di direttiva nginx nella cache (bypass / no_cache) attiva per le risposte fastcgi?

In nginx '1.0.3 Changenotes hanno risolto un danneggiamento della risposta:

Bugfix: una risposta memorizzata nella cache potrebbe essere interrotta se i valori della direttiva "proxy / fastcgi / scgi / uwsgi_cache_bypass" e "proxy / fastcgi / scgi / uwsgi_no_cache" fossero diversi; il bug era apparso in 0.8.46.

Fonte: http://nginx.org/en/CHANGES (1.0.3. Sezione)


0

Forse la corruzione occasionale si verifica solo se l'output contiene almeno un carattere UTF-8.

La lunghezza del contenuto e la stringa non sono la stessa cosa, poiché un carattere UTF-8 può contenere da 2 a 5 byte.


Hmmmm .. mentre questo è vero, non sembra essere la causa perché la corruzione si stava verificando nel mezzo dei blocchi di dati e non era semplicemente un caso di dati mancanti alla fine.
glenc,

0

Un modo per risolvere un po 'di più questo caso sarebbe:

  • avere nginx e django in esecuzione su hardware diverso (in modo da poter catturare facilmente il traffico)
  • acquisire il traffico dal client a - / -> nginx e nginx - / -> django (ad esempio utilizzare WireShark)

Una volta rilevato un errore sul lato client (basato su sha1), vai all'acquisizione di rete, guarda nel flusso registrato (TCP) e prova a scoprire se il problema è generato da nginx o proviene (direttamente) da Django .


0

Ho avuto un problema molto simile che mi ha tormentato fino a quando ho avuto questa configurazione. Come te, utilizzo FastCGI, Nginx e macOS e ho riscontrato una corruzione casuale nel mezzo di richieste di grandi dimensioni (era circa il 2% delle richieste di un documento da 1,5 MB).

Sono stato in grado di risolvere il mio problema passando a socket Unix su TCP per la connessione FastCGI tra PHP-FPM (nel mio caso) e Nginx. Non so quale parte del puzzle sia responsabile della corruzione, ma evitare la connessione TCP interna l'ha risolto.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.