nginx + fastCGI + Django - Datenbeschädigung in den an den Client gesendeten Antworten

10

Ich verwende Django hinter Nginx mit FastCGI. Ich habe festgestellt, dass in einigen der an den Client gesendeten Antworten zufällige Datenbeschädigungen in der Mitte der Antworten auftreten (möglicherweise einige hundert Bytes in der Mitte).

Zu diesem Zeitpunkt habe ich mich darauf beschränkt, entweder ein Fehler im FastCGI-Handler von nginx oder im FastCGI-Handler von Django zu sein (dh wahrscheinlich ein Fehler in flup), da dieses Problem nie auftritt, wenn ich den Django-Server im Standalone- runserverModus (dh ) ausführe . Dies geschieht nur im FastCGI-Modus.

Weitere interessante Trends:

  • Dies tritt tendenziell bei größeren Antworten auf. Wenn sich ein Client zum ersten Mal anmeldet, werden ihm mehrere 1-MB-Blöcke gesendet, um sie mit der Server-Datenbank zu synchronisieren. Nach dieser ersten Synchronisierung sind die Antworten viel kleiner (normalerweise jeweils einige KB). Die Beschädigung scheint immer bei den zu Beginn gesendeten 1-MB-Blöcken aufzutreten.

  • Dies tritt häufiger auf, wenn der Client über LAN mit dem Server verbunden ist (dh eine Verbindung mit geringer Latenz und hoher Bandbreite). Dies lässt mich denken, dass es in Nginx oder Flup eine Art Race-Bedingung gibt, die durch eine erhöhte Datenrate verschärft wird.

Im Moment musste ich das umgehen, indem ich einen zusätzlichen SHA1-Digest in den Antwortheader einfügte und den Client Antworten ablehnen ließ, bei denen der Header nicht mit der Body-Prüfsumme übereinstimmt, aber dies ist eine schreckliche Lösung.

Hat jemand anderes so etwas erlebt oder hat er Hinweise, wie man feststellen kann, ob Flup oder Nginx hier schuld sind, damit ich einen Fehler beim entsprechenden Team melden kann?

Vielen Dank im Voraus für jede Hilfe.

Hinweis: Ich habe vor einiger Zeit auch einen ähnlichen Fehler in lighttpd + FastCGI + Django hier gepostet: /programming/3714489/lighttpd-fastcgi-django-truncated-response-sent-to-client-due-to -unerwartet ... obwohl dies nicht dasselbe ist (Kürzung vs. Korruption), sieht es so aus, als ob der übliche Schuldige eher flup / Django als der Webserver ist.

Bearbeiten: Ich sollte auch beachten, was meine Umgebung ist:

  • OSX 10.6.6 auf einem Mac Mini

  • Python 2.6.1 (System)

  • Django 1.3 (vom offiziellen Tarball)

  • flup 1.0.2 (aus Python-Ei auf der Flup-Seite)

  • nginx + ssl 1.0.0 (von Macports)

BEARBEITEN: Als Antwort auf Jerzyks Kommentar sieht der Codepfad, der die Antwort zusammenstellt, so aus (aus Gründen der Prägnanz bearbeitet):

# 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

Ich denke nicht, dass es möglich ist, dass die Inhaltslänge aufgrund dessen falsch ist, und AFAIK gibt es keine Möglichkeit, ein Django HttpResponse-Objekt als explizit binär im Gegensatz zu Text zu markieren. Da das Problem nur zeitweise auftritt, glaube ich nicht, dass es das erklärt, sonst würden Sie es vermutlich bei jeder Anfrage sehen.

EDIT @ionelmc: Sie müssen die Inhaltslänge in Django festlegen - nginx legt dies nicht für Sie fest, wie im folgenden Beispiel gezeigt, nachdem ich die explizite Einstellung der Inhaltslänge deaktiviert habe:

$ 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
quelle
Wenn sich die anfänglichen Blöcke nicht oft ändern oder nicht benutzerspezifisch sind, ist es vielleicht besser, auf die Festplatte zu schreiben und direkt über Nginx zu bedienen?
sunn0
Leider sind die Chunks sowohl benutzerspezifisch als auch häufig wechselnd, sodass für diese Anwendung kein Caching dieser Art geeignet wäre. Ich möchte auch herausfinden, was diese Datenbeschädigung tatsächlich verursacht, anstatt sie nur zu umgehen (was ich bereits mit dem zusätzlichen SHA1-Digest im Header mache).
Glenc
Ich kann mir zwei mögliche Gründe vorstellen: falsche Codierung - HttpRespose als Text vs. binäre oder falsche Header (insbesondere Inhaltslänge)
Jerzyk
1
@glenc Was ist ein Inhaltstyp für diese Antwort? Wenn dies binär ist - können Sie versuchen, es einzustellen? (zB mimetype = 'application / x-ms-excel' oder sonst)
Jerzyk
2
Sie müssen die Inhaltslänge nicht festlegen, wenn Ihre Übertragungscodierung aufgeteilt ist. rfc 2616 verbietet dies ausdrücklich: "Das Headerfeld" Content-Length "darf NICHT gesendet werden, wenn diese beiden Längen unterschiedlich sind (dh wenn ein Headerfeld" Transfer-Encoding "vorhanden ist)."
Ionelmc

Antworten:

1

Haben Sie irgendeine Art von Nginx-Caching-Direktive (Bypass / No_Cache), die für die Fastcgi-Antworten aktiv ist?

In nginx '1.0.3 Changenotes wurde eine Antwortbeschädigung behoben:

Bugfix: Eine zwischengespeicherte Antwort kann unterbrochen werden, wenn die Direktivenwerte "proxy / fastcgi / scgi / uwsgi_cache_bypass" und "proxy / fastcgi / scgi / uwsgi_no_cache" unterschiedlich waren. Der Fehler war in 0.8.46 aufgetreten.

Quelle: http://nginx.org/en/CHANGES (Abschnitt 1.0.3)

Michel Feldheim
quelle
0

Möglicherweise tritt die gelegentliche Beschädigung nur auf, wenn die Ausgabe mindestens ein UTF-8-Zeichen enthält.

Inhaltslänge und Zeichenfolgenlänge sind nicht dasselbe, da ein UTF-8-Zeichen 2 bis 5 Byte enthalten kann.

Andy Lee Robinson
quelle
Hmmmm .. obwohl dies wahr ist, scheint es nicht die Ursache zu sein, da die Beschädigung in der Mitte der Datenblöcke stattfand und nicht einfach ein Fall von fehlenden Daten am Ende war.
Glenc
0

Eine Möglichkeit, diesen Fall ein bisschen mehr zu beheben, wäre:

  • Nginx und Django laufen auf unterschiedlicher Hardware (damit Sie den Datenverkehr problemlos erfassen können).
  • Erfassen Sie den Datenverkehr vom Client zu - / -> Nginx und Nginx - / -> Django (dh verwenden Sie Wireshark).

Wenn Sie auf der Clientseite einen Fehler festgestellt haben (basierend auf sha1), gehen Sie zur Netzwerkerfassung, sehen Sie sich den aufgezeichneten (TCP) Stream an und versuchen Sie herauszufinden, ob das Problem von nginx generiert wird oder ob es (direkt) von django stammt .

cipy
quelle
0

Ich hatte ein sehr ähnliches Problem, das mich so lange plagte, wie ich dieses Setup hatte. Wie Sie verwende ich FastCGI, Nginx und macOS und habe bei großen Anfragen zufällige Beschädigungen festgestellt (dies waren ungefähr 2% der Anfragen eines 1,5-MB-Dokuments).

Ich konnte mein Problem lösen, indem ich für die FastCGI-Verbindung zwischen PHP-FPM (in meinem Fall) und Nginx zu Unix-Sockets über TCP wechselte. Ich weiß nicht, welcher Teil des Puzzles für die Beschädigung verantwortlich ist, aber das Vermeiden der internen TCP-Verbindung hat das Problem behoben.

Robert
quelle