Soll ich bei Python bleiben oder Python aufgeben, um die Parallelität zu bewältigen?


31

Ich habe ein 10K- LOC- Projekt in Django geschrieben, das eine ganze Menge Sellerie ( RabbitMQ ) für Asynchronität und Hintergrundjobs enthält, und bin zu dem Schluss gekommen, dass Teile des Systems davon profitieren würden, wenn sie zur besseren Parallelität in etwas anderem als Django umgeschrieben würden . Gründe sind:

  • Signalhandhabung und veränderbare Objekte. Besonders wenn ein Signal ein anderes auslöst, kann es überraschend sein , diese in Django mit dem ORM zu verarbeiten, wenn sich Instanzen ändern oder verschwinden. Ich möchte einige Messaging - Ansatz verwenden , in denen die Daten weitergegeben entlang ändert sich nicht in einem Handler ( Clojure die Copy-on-Write - Ansatz scheint nett, wenn ich es richtig verstanden habe).
  • Teile des Systems sind nicht webbasiert und benötigen eine bessere Unterstützung für die gleichzeitige Ausführung von Aufgaben. Das System liest beispielsweise NFC- Tags, und wenn eines gelesen wird, leuchtet eine LED einige Sekunden lang (Sellerie-Task), es wird ein Ton abgespielt (andere Sellerie-Task) und die Datenbank wird abgefragt (andere Task). Dies wird als Django-Verwaltungsbefehl implementiert, aber Django und sein ORM sind von Natur aus synchron und teilen sich nur begrenzt Speicher (wir denken darüber nach, weitere NFC-Leser hinzuzufügen, und ich denke nicht, dass der Django + Celery-Ansatz länger funktioniert Ich würde gerne bessere Nachrichtenübermittlungsfunktionen sehen.

Was sind die Vor- und Nachteile von Twisted oder Tornado im Vergleich zu einer Sprache wie Erlang oder Clojure ? Ich interessiere mich für praktische Vorteile und Nachteile.

Wie sind Sie zu dem Schluss gekommen, dass einige Teile des Systems in einer anderen Sprache besser abschneiden würden? Leiden Sie unter Leistungsproblemen? Wie gravierend sind diese Probleme? Wenn es schneller sein kann, ist es wichtig, dass es schneller ist?

Beispiel 1: Django bei der Arbeit außerhalb einer HTTP-Anfrage:

  1. Ein NFC-Tag wird gelesen.
  2. Die Datenbank (und möglicherweise LDAP) wird abgefragt, und wir möchten etwas tun, wenn Daten verfügbar werden (rotes oder grünes Licht, Sound abspielen). Dies blockiert mit dem Django ORM, aber solange Sellerie-Arbeiter verfügbar sind, spielt es keine Rolle. Kann ein Problem mit mehr Stationen sein.

Beispiel 2: "Message-Passing" mit Django-Signalen:

  1. Ein post_deleteEreignis wird behandelt, andere Objekte können dadurch verändert oder gelöscht werden.
  2. Am Ende sollten Benachrichtigungen an die Benutzer gesendet werden. Hier wäre es schön, wenn Argumente, die an den Benachrichtigungs-Handler übergeben werden, Kopien von gelöschten oder zu löschenden Objekten wären und garantiert nicht im Handler geändert würden. (Dies kann manuell erfolgen, indem die vom ORM verwalteten Objekte natürlich nicht an die Handler übergeben werden.)

Ich denke, bessere Antworten werden sich ergeben, wenn Sie näher erläutern, warum Sie zu dem Schluss gekommen sind
Winston Ewert,

5
Bevor jemand sagt, dass Fragen zur Sprachauswahl nicht zum Thema gehören, möchte ich sagen, dass dies in Ordnung ist, da es sich um ein praktisches Problem mit bestimmten Anforderungen handelt. Ich hoffe, es werden einige detaillierte Vergleiche gezogen.
Adam Lear

Twisted ist das Gegenteil von Concurrent! Es ist ein einzelner Thread-Server, der ereignisgesteuert ist. Wenn Sie echte Parallelität benötigen, werden Sie nicht weitergeleitet.

Antworten:


35

Eröffnungsgedanken

Wie sind Sie zu dem Schluss gekommen, dass einige Teile des Systems in einer anderen Sprache besser abschneiden würden? Leiden Sie unter Leistungsproblemen? Wie gravierend sind diese Probleme? Wenn es schneller sein kann, ist es wichtig, dass es schneller ist?

Single-Thread-Asynchronität

Es gibt verschiedene Fragen und andere Webressourcen, die sich bereits mit den Unterschieden, Vor- und Nachteilen von Single-Thread-Asynchronität und Multi-Thread-Parallelität befassen. Es ist interessant zu lesen, wie sich das asynchrone Single-Thread-Modell von Node.js verhält, wenn E / A der größte Engpass ist und viele Anfragen gleichzeitig bearbeitet werden.

Twisted-, Tornado- und andere asynchrone Modelle nutzen einen Single-Thread hervorragend. Da in vielen Webprogrammen viele E / A-Vorgänge (Netzwerk, Datenbank usw.) ausgeführt werden, summiert sich die Wartezeit für Remoteanrufe erheblich. In dieser Zeit könnten Sie andere Dinge erledigen - z. B. andere Datenbankaufrufe starten, Seiten rendern und Daten generieren. Die Auslastung dieses Single-Threads ist extrem hoch.

Einer der größten Vorteile der Singlethread-Asynchronität besteht darin, dass viel weniger Arbeitsspeicher benötigt wird. Bei der Ausführung mit mehreren Threads benötigt jeder Thread eine bestimmte Menge an reserviertem Speicher. Wenn die Anzahl der Threads zunimmt, nimmt auch die Speichermenge zu, die nur für das Vorhandensein der Threads erforderlich ist. Da der Speicher endlich ist, gibt es Grenzen für die Anzahl der Threads, die gleichzeitig erstellt werden können.


Beispiel

Stellen Sie sich im Fall eines Webservers vor, dass jede Anforderung einen eigenen Thread erhält. Angenommen, für jeden Thread ist 1 MB Arbeitsspeicher erforderlich, und der Webserver verfügt über 2 GB RAM. Dieser Webserver kann zu jedem Zeitpunkt (ungefähr) 2000 Anforderungen verarbeiten, bevor nicht mehr genügend Arbeitsspeicher für die Verarbeitung vorhanden ist.

Wenn Ihre Auslastung wesentlich höher ist, wird es sehr lange dauern, bis die Anforderungen abgeschlossen sind (wenn Sie darauf warten, dass ältere Anforderungen ausgeführt werden), oder Sie müssen mehr Server in den Cluster werfen, um die Anzahl der gleichzeitig möglichen Anforderungen zu erhöhen .


Multi-Thread-Parallelität

Die gleichzeitige Ausführung mehrerer Threads erfordert stattdessen die gleichzeitige Ausführung mehrerer Aufgaben. Das heißt, wenn ein Thread blockiert ist und auf die Rückgabe eines Datenbankaufrufs wartet, können gleichzeitig andere Anforderungen verarbeitet werden. Die Thread-Auslastung ist geringer, aber die Anzahl der ausgeführten Threads ist viel größer.

Multi-Thread-Code ist auch viel schwieriger zu überlegen. Es gibt Probleme mit dem Sperren, der Synchronisierung und anderen unterhaltsamen Problemen bei der gemeinsamen Nutzung. Single-Thread-Asynchronität leidet nicht unter denselben Problemen.

Multi-Thread-Code ist jedoch für CPU-intensive Aufgaben viel leistungsfähiger . Wenn es für einen Thread keine Möglichkeit gibt, "nachzugeben" - wie bei einem Netzwerkaufruf, der normalerweise blockiert würde -, hat ein Single-Thread-Modell keinerlei Parallelität.

Beide können koexistieren

Es gibt natürlich eine Überlappung zwischen den beiden; sie schließen sich nicht aus. Beispielsweise kann Multithread-Code nicht blockierend geschrieben werden, um jeden Thread besser zu nutzen.


Die Quintessenz

Es gibt noch viele andere Punkte zu beachten, aber ich denke gerne über diese beiden Punkte nach:

  • Wenn Ihr Programm E / A-gebunden ist , wird die Single-Thread-Asynchronität wahrscheinlich recht gut funktionieren.
  • Wenn Ihr Programm CPU-gebunden ist , ist ein Multithread-System wahrscheinlich das Beste.

In Ihrem speziellen Fall müssen Sie bestimmen, welche Art von asynchroner Arbeit ausgeführt wird und wie oft diese Aufgaben auftreten.

  • Treten sie bei jeder Anfrage auf? In diesem Fall wird der Speicher wahrscheinlich zu einem Problem, wenn die Anzahl der Anforderungen steigt.
  • Sind diese Aufgaben bestellt? In diesem Fall müssen Sie die Synchronisierung in Betracht ziehen, wenn Sie mehrere Threads verwenden.
  • Sind diese Aufgaben CPU-intensiv? Wenn ja, kann ein einzelner Thread mit der Last mithalten?

Es gibt keine einfache Antwort. Sie müssen Ihre Anwendungsfälle berücksichtigen und entsprechend gestalten. Manchmal ist ein asynchrones Single-Thread-Modell besser. In anderen Fällen ist die Verwendung einer Reihe von Threads erforderlich, um eine massive Parallelverarbeitung zu erreichen.

Andere Überlegungen

Es gibt noch andere Aspekte, die Sie berücksichtigen müssen, und nicht nur das von Ihnen gewählte Parallelitätsmodell. Kennen Sie Erlang oder Clojure? Glauben Sie, dass Sie in der Lage sind, sicheren Multithread-Code in einer dieser Sprachen zu schreiben, um die Leistung Ihrer Anwendung zu verbessern? Wird es lange dauern, bis Sie mit einer dieser Sprachen vertraut sind, und wird die Sprache, die Sie lernen, Ihnen in Zukunft zugute kommen?

Wie steht es mit den Kommunikationsschwierigkeiten zwischen diesen beiden Systemen? Wird es zu komplex sein, zwei separate Systeme parallel zu warten? Wie erhält das Erlang-System Aufgaben von Django? Wie wird Erlang diese Ergebnisse an Django zurückmelden? Ist die Leistung so bedeutend, dass sich die zusätzliche Komplexität lohnt?


Abschließende Gedanken

Ich habe immer festgestellt, dass Django schnell genug ist und von einigen sehr stark frequentierten Websites verwendet wird. Sie können verschiedene Leistungsoptimierungen vornehmen, um die Anzahl der gleichzeitigen Anforderungen und die Antwortzeit zu erhöhen. Zugegeben, ich habe bisher noch nichts mit Celery gemacht, sodass die üblichen Leistungsoptimierungen wahrscheinlich keine Probleme lösen werden, die bei diesen asynchronen Aufgaben auftreten können.

Natürlich gibt es immer den Vorschlag, das Problem mit mehr Hardware zu lösen. Sind die Kosten für die Bereitstellung eines neuen Servers günstiger als die Entwicklungs- und Wartungskosten eines völlig neuen Subsystems?

Ich habe zu diesem Zeitpunkt viel zu viele Fragen gestellt, aber das war meine Absicht. Die Antwort wird ohne Analyse und weitere Details nicht einfach sein. In der Lage zu sein, die Probleme zu analysieren, bedeutet jedoch, die zu stellenden Fragen zu kennen. Hoffentlich habe ich dabei geholfen.

Mein Bauchgefühl sagt, dass ein Umschreiben in einer anderen Sprache nicht notwendig ist. Die Komplexität und die Kosten werden wahrscheinlich zu groß sein.


Bearbeiten

Reaktion auf das Follow-up

Ihr Follow-up präsentiert einige sehr interessante Anwendungsfälle.


1. Django arbeitet außerhalb von HTTP-Anfragen

In Ihrem ersten Beispiel haben Sie NFC-Tags gelesen und anschließend die Datenbank abgefragt. Ich denke nicht, dass das Schreiben dieses Teils in einer anderen Sprache für Sie so nützlich sein wird, da das Abfragen der Datenbank oder eines LDAP-Servers an die Netzwerk-E / A (und möglicherweise die Datenbankleistung) gebunden ist. Andererseits wird die Anzahl der gleichzeitigen Anforderungen vom Server selbst festgelegt, da jeder Verwaltungsbefehl als eigener Prozess ausgeführt wird. Es wird Setup- und Teardown-Zeiten geben, die sich auf die Leistung auswirken, da Sie keine Nachrichten an einen bereits laufenden Prozess senden. Sie können jedoch mehrere Anforderungen gleichzeitig senden, da es sich jeweils um einen isolierten Prozess handelt.

Für diesen Fall sehe ich zwei Möglichkeiten, die Sie untersuchen können:

  1. Stellen Sie sicher, dass Ihre Datenbank mit dem Verbindungspooling mehrere Abfragen gleichzeitig verarbeiten kann. (Oracle erfordert beispielsweise, dass Sie Django entsprechend konfigurieren 'OPTIONS': {'threaded':True}.) Auf Datenbank- oder Django-Ebene gibt es möglicherweise ähnliche Konfigurationsoptionen, die Sie für Ihre eigene Datenbank anpassen können. Unabhängig davon, in welcher Sprache Sie Ihre Datenbankabfragen schreiben, müssen Sie auf die Rückgabe dieser Daten warten, bevor Sie die LEDs aufleuchten lassen können. Die Leistung des Abfragecodes kann jedoch einen Unterschied machen, und der Django ORM ist nicht blitzschnell ( aber normalerweise schnell genug).
  2. Rüst- / Abbauzeit minimieren. Haben Sie einen ständig laufenden Prozess und senden Sie Nachrichten an ihn. (Korrigieren Sie mich, wenn ich falsch liege, aber genau darauf konzentriert sich Ihre ursprüngliche Frage.) Ob dieser Prozess in Python / Django oder in einer anderen Sprache / einem anderen Framework geschrieben ist, wird oben behandelt. Mir gefällt die Idee nicht, Verwaltungsbefehle so häufig zu verwenden. Ist es möglich, dass ständig ein kleines Stück Code läuft, das Nachrichten von den NFC-Lesern in die Nachrichtenwarteschlange schiebt, die Sellerie dann liest und an Django weiterleitet? Das Einrichten und Herunterfahren eines kleinen Programms, auch wenn es in Python geschrieben ist (aber nicht in Django!), Sollte besser sein, als ein Django-Programm (mit all seinen Subsystemen) zu starten und zu stoppen.

Ich bin mir nicht sicher, welchen Webserver Sie für Django verwenden. mod_wsgiMit for Apache können Sie die Anzahl der Prozesse und Threads in den Prozessen konfigurieren, für die Serviceanforderungen gestellt werden. Stellen Sie sicher, dass Sie die entsprechende Konfiguration Ihres Webservers anpassen, um die Anzahl der wartbaren Anforderungen zu optimieren.


2. "Message-Passing" mit Django-Signalen

Ihr zweiter Anwendungsfall ist ebenfalls ziemlich interessant. Ich bin mir nicht sicher, ob ich die Antworten dafür habe. Wenn Sie Modellinstanzen löschen und sie später bearbeiten möchten, können Sie sie möglicherweise serialisieren JSON.dumpsund anschließend deserialisieren JSON.loads. Es ist später unmöglich, das Objektdiagramm vollständig neu zu erstellen (Abfragen verwandter Modelle), da verwandte Felder nur schleppend aus der Datenbank geladen werden und diese Verknüpfung nicht mehr besteht.

Die andere Möglichkeit wäre, ein Objekt zum Löschen zu markieren und erst am Ende des Anforderungs- / Antwortzyklus zu löschen (nachdem alle Signale bearbeitet wurden). Möglicherweise ist ein benutzerdefiniertes Signal erforderlich, um dies zu implementieren, anstatt sich darauf zu verlassen post_delete.


1
Viele FUDs und Zweifel an Sperren und anderen Dingen, die nicht mit Erlang in Zusammenhang stehen. Keines der von Ihnen aufgelisteten traditionellen Probleme mit gemeinsamem Status ist eine Überlegung mit einer Sprache und Laufzeit, die speziell darauf ausgelegt sind, den Status nicht gemeinsam zu nutzen. Erlang kann Zehntausende von diskreten Prozessen mit sehr wenig RAM bewältigen, auch der Speicherdruck spielt keine Rolle.

@ Jarrod, ich persönlich kenne Erlang nicht, also werde ich akzeptieren, was Sie in dieser Hinsicht sagen. Ansonsten ist fast alles, was ich erwähnte, relevant. Kosten, Komplexität und ob die aktuellen Tools richtig genutzt werden oder nicht.
Josh Smeaton


Das ist die Art von epischer Antwort, die ich wirklich gerne lese ^^. +1, gute Arbeit!
Laurent Bourgault-Roy

Auch wenn Sie haben Djangos Vorlagen können sie in erlang mit Erlydtl verwendet werden
Zachary K

8

Ich habe eine sehr hoch entwickelte, hoch skalierbare Entwicklung für einen großen US- ISP durchgeführt . Wir haben einige ernstzunehmende Transaktionsnummern mit einem Twisted- Server erstellt, und es war ein Albtraum der Komplexität, Python / Twisted auf alles skalieren zu lassen, was CPU-gebunden war . Die E / A-Bindung ist kein Problem, aber die CPU-Bindung war unmöglich. Wir konnten Systeme schnell zusammenstellen, aber die Skalierung auf Millionen gleichzeitiger Benutzer war ein Albtraum von Konfiguration und Komplexität, wenn sie an die CPU gebunden waren.

Ich habe einen Blog-Beitrag darüber geschrieben, Python / Twisted VS Erlang / OTP .

TLDR; Erlang hat gewonnen.


4

Praktische Probleme mit Twisted (die ich liebe und seit ungefähr fünf Jahren benutze):

  1. Die Dokumentation lässt zu wünschen übrig und das Modell ist ohnehin recht komplex zu erlernen. Ich finde es schwierig, andere Python-Programmierer dazu zu bringen, an Twisted-Code zu arbeiten.
  2. Am Ende habe ich die Datei-E / A und den Datenbankzugriff blockiert, da keine guten Blockierungs-APIs vorhanden waren. Dies kann die Leistung erheblich beeinträchtigen.
  3. Es scheint keine große und gesunde Community zu geben, die Twisted einsetzt. Beispielsweise hat Node.js eine viel aktivere Entwicklung, insbesondere für die Web-Back-End-Programmierung.
  4. Es ist immer noch Python, und zumindest ist CPython nicht das Schnellste.

Ich habe ein bisschen mit Node.js mit CoffeeScript gearbeitet, und wenn die gleichzeitige Leistung Ihr Anliegen ist, ist dies möglicherweise den Sprung wert.

Haben Sie darüber nachgedacht, mehrere Instanzen von Django auszuführen, um Clients auf mehrere Instanzen zu verteilen?


1
Python-Dokumentation im Allgemeinen lässt zu wünschen übrig: / (Nicht, dass es so schlimm ist, aber für eine Sprache , die so beliebt ist , würde man erwarten, dass sie viel besser ist).
Turm

3
Ich finde, dass die Python-Dokumentation und insbesondere die Django-Dokumentation zu den besten Dokumenten für jede Sprache gehören. Viele Bibliotheken von Drittanbietern lassen jedoch zu wünschen übrig.
Josh Smeaton

1

Ich schlage Folgendes vor, bevor Sie in eine andere Sprache wechseln.

  1. Verwenden Sie LTTng zum Aufzeichnen von Systemereignissen wie Seitenfehlern, Kontextwechseln und Systemaufrufwarten.
  2. Konvertieren Sie, wann immer es zu lange dauert, um die C-Bibliothek zu verwenden, und verwenden Sie ein beliebiges Entwurfsmuster (Multithreading, signalereignisbasiert, Rückruf asynchron oder Unix traditionell select), das dort für die E / A gut ist.

Ich würde Threading in Python nicht verwenden, wenn die Anwendung in der Leistung Priorität hat. Ich würde die obige Option wählen, die viele Probleme wie Wiederverwendung von Software, Konnektivität mit Django , Leistung, einfache Entwicklung usw. lösen kann .

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.