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:
- Ein NFC-Tag wird gelesen.
- 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:
- Ein
post_delete
Ereignis wird behandelt, andere Objekte können dadurch verändert oder gelöscht werden. - 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.)
quelle
Antworten:
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:
In Ihrem speziellen Fall müssen Sie bestimmen, welche Art von asynchroner Arbeit ausgeführt wird und wie oft diese Aufgaben auftreten.
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:
'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).Ich bin mir nicht sicher, welchen Webserver Sie für Django verwenden.
mod_wsgi
Mit 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.dumps
und anschließend deserialisierenJSON.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
.quelle
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.
quelle
Praktische Probleme mit Twisted (die ich liebe und seit ungefähr fünf Jahren benutze):
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?
quelle
Ich schlage Folgendes vor, bevor Sie in eine andere Sprache wechseln.
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 .
quelle