2013-09-02 3 views
22

Kann jemand erklären, was der Engpass jeder Nebenläufigkeitsmethode ist?Was profitieren VS-Multi-Thread-Server mit mehreren Prozessen am meisten?

Servern wie Unicorn (Prozess basiert) ein Puma (Gewinde basiert).

Werden CPU-Kerne bevorzugt? Fäden? oder einfach taktgeschwindigkeit? oder eine spezielle Kombination?

Wie ermittelt man die optimalen CPU-Eigenschaften, die für den Einsatz dedizierter Server benötigt werden?

und wie die beste Arbeitermenge im Falle von Unicorn oder Threads Menge im Fall von Puma zu bestimmen?

Antwort

54

Unicorn ist prozessbasiert, was bedeutet, dass jede Instanz von Ruby in einem eigenen Prozess existieren muss. Dies kann für jeden Prozess im Bereich von 500 MB liegen, was Systemressourcen schnell entlastet. Puma, das thread-basiert ist, wird nicht die gleiche Menge an Speicher verwenden, um THEORETISCH die gleiche Menge an Nebenläufigkeit zu erreichen.

Einhorn, da mehrere Prozesse ausgeführt werden, wird Parallelität zwischen den verschiedenen Prozessen haben. Dies wird durch Ihre CPU-Kerne begrenzt (mehr Kerne können mehr Prozesse zur gleichen Zeit ausführen), aber der Kernel wechselt zwischen aktiven Prozessen, so dass mehr als 4 oder 8 Prozesse (wie viele Kerne Sie haben) ausgeführt werden können. Sie werden durch den Speicher Ihrer Maschine eingeschränkt. Bis vor kurzem war Ruby nicht kopierfreundlich, was bedeutet, dass jeder Prozess seinen eigenen geerbten Speicher hatte (Unicorn ist ein Preforking-Server). Ruby 2.0 ist copy-on-write friendly, was bedeuten könnte, dass Unicorn nicht alle untergeordneten Prozesse im Speicher laden muss. Ich bin da nicht 100% ig klar. Lesen Sie mehr über copy on write und werfen Sie einen Blick auf jessie storimers fantastisches Buch 'Working with unix processes'. Ich bin mir ziemlich sicher, dass er es dort behandelt hat.

Puma ist ein Thread-Server. MRI Ruby kann wegen der globalen Interpretersperre (GIL) nur eine einzelne CPU-gebundene Aufgabe gleichzeitig ausführen (vgl. Ruby Tapas-Episode 127, Parallel-Fib). Es wird ein Kontextwechsel zwischen den Threads stattfinden, aber solange es eine CPU-gebundene Task (z. B. Datenverarbeitung) ist, wird es immer nur einen einzelnen Ausführungsthread ausführen. Dies wird interessant, wenn Sie Ihren Server mit einer anderen Implementierung von Ruby ausführen, wie JRuby oder Rubinius. Sie haben nicht die GIL und können viele Informationen parallel verarbeiten. JRuby ist ziemlich schnell, und während Rubinius im Vergleich zur MRT langsam ist, verarbeitet Multithreading Rubinius Daten schneller als MRT. Während der nicht blockierenden E/A jedoch (z. B. Schreiben in eine Datenbank, Erstellen einer Webanforderung) wechselt der MRT-Kontext zu einem nicht ausführenden Thread und funktioniert dort und wechselt dann zurück zum vorherigen Thread, wenn Informationen zurückgegeben wurden.

Für Unicorn würde ich sagen, der Flaschenhals ist Speicher und Taktgeschwindigkeit. Für Puma, würde ich sagen, der Engpass ist Ihre Wahl des Dolmetschers (MRI vs Rubinius oder JRuby) und die Art der Arbeit, die Ihr Server macht (viele cpu gebundene Aufgaben vs non-blocking IO).

Es gibt Tonnen von großen Ressourcen in dieser Debatte. Sieh dir Jessie Storimers Bücher zu diesen Themen an: working with ruby threads und working with unix processes; read this quick summary of preforking servers von Ryan Tomayko, und google um für weitere Informationen.

Ich weiß nicht, was die beste Arbeitermenge für Unicorn oder Puma in Ihrem Fall ist. Das beste, was Sie tun können, ist führen Sie Leistungstests durch und tun Sie, was für Sie richtig ist. Es gibt keine Einheitsgröße für alle.(obwohl ich denke, dass der Puma-Standard ist, einen Pool von 16 Threads zu verwenden und es dabei zu sperren)

+0

Danke Stuart für die tolle Aufklärung !! – Ryan

+0

Rubin 2.0 "Kopie bei Schreibfreundlich" bedeutet, dass der GC die Freigabe nicht mit der Kopie auf Schreibseiten im Heap teilt. Mit 1.9 würden Sie eine Reihe von Einhörnern abzweigen, und der GC würde Objekte ausführen und verschieben und alle COW-Sharing-Vorteile, die Sie möglicherweise zwischen diesen Prozessen hatten, bevor der GC gestartet wurde, vollständig verwerfen. – lamont

3

Puma ist tatsächlich Multithreaded und Multiprozess. Sie können es im "Clustered-Modus" aufrufen, wo es mehrere gegabelte Arbeiter hervorbringt, die auf verschiedenen Kernen im MRT laufen. Da Puma Multithread ist, ist es wahrscheinlich angemessen, eine Anzahl von Prozessen gleich der Anzahl der Kerne auf dem Server auszuführen.

puma -t 8:32 -w 4 --preload 

Dies wird handhaben bis zu 32 gleichzeitigen Threads, mit bis zu 4 Threads gleichzeitig auf dem CPUs laufen und soll die CPU-Ressourcen auf die zu maximieren fähig sein: So für ein 4-Kern-Server so etwas wie dies angemessen wäre, Server. Das Argument --preload lädt die App vor und nutzt die Vorteile von Ruby 2.0 COW für die Garbage Collection, um die RAM-Nutzung zu reduzieren.

Wenn Ihre App viel Zeit darauf verwendet, auf andere Dienste (Suchdienste, Datenbanken usw.) zu warten, wird dies eine große Verbesserung darstellen. Wenn ein Thread blockiert, kann ein anderer Thread in demselben Prozess die CPU greifen und funktionieren. Sie können in diesem Beispiel bis zu 32 Anforderungen parallel unterstützen, während Sie nur 4 Prozesse im RAM ausführen.

Mit Unicorn müssten Sie 32 Arbeiter abzweigen, die 32 Prozesse im RAM ausführen würden, was sehr verschwenderisch ist.

Wenn Ihre ganze App CPU-Knirschen ausführt, dann wird dies sehr ineffizient sein, und Sie sollten die Anzahl der Einhörner reduzieren und die Vorteile von Puma gegenüber Unicorn reduzieren. Aber im Unicorn-Fall musst du deine App benchmarken und die richtige Zahl herausfinden. Puma tendiert dazu, sich selbst zu optimieren, indem es mehr Threads hervorbringt, und seine Leistung sollte von nicht schlechter als Unicorn (im reinen CPU-Fall) bis zu weit besser als Unicorn reichen (im Falle einer App, die viel schläft).

Natürlich, wenn Sie Rubinius oder JRuby verwenden, dann ist es kein Wettbewerb, und Sie können einen Prozess spawnen, der Multicore ausführt und alle 32 Threads behandelt.

TL; DR ist, dass ich denke, dass Unicorn gegenüber Puma keinen großen Vorteil hat, da Puma tatsächlich beide Modelle verwendet.

Natürlich weiß ich nichts über die Zuverlässigkeit von Puma vs Unicorn in laufenden Produktionssoftware in der realen Welt. Eine Sache, die Sie beachten müssen, ist, dass wenn Sie über einen globalen Status in einem Thread kritzeln, dies andere Anforderungen beeinflussen kann, die zur gleichen Zeit ausgeführt werden, was zu unbestimmten Ergebnissen führen kann. Da Unicorn keine Threads verwendet, gibt es keine Nebenläufigkeitsprobleme. Ich würde hoffen, dass Puma und Rails zu diesem Zeitpunkt bereits reif für Concurrency-Probleme sind und dass Puma in der Produktion einsetzbar ist. Allerdings würde ich nicht unbedingt davon ausgehen, dass jedes rails-plugin und rubygem, das ich auf GitHub gefunden habe, threadsicher ist, und ich würde erwarten, dass ich etwas zusätzliche Arbeit machen muss. Aber sobald Sie genug Erfolg haben, Threading-Probleme in Bibliotheken von Drittanbietern zu finden, sind Sie wahrscheinlich groß genug, dass Sie sich die RAM-Kosten für das Ausführen so vieler Unicorn-Prozesse nicht leisten können. OTOH, ich verstehe Nebenläufigkeit Bugs und ich bin gut mit Ruby, so dass die Debugging-Kosten viel weniger für mich als die Kosten für den Kauf von RAM in der Cloud sein können. YMMV.

Beachten Sie auch, dass ich nicht sicher bin, ob Sie Hyperthread-Kerne oder physische Kerne bei der Schätzung des Werts für die Übergabe an "-w" zählen und Sie selbst testen müssen und welche Werte zu verwenden sind für -t. Selbst wenn Sie die Anzahl der Prozesse, die Sie benötigen, doppelt ausführen, sollte der Prozessplaner im Kernel in der Lage sein, dies ohne Probleme zu bewältigen, bis Sie die CPU gesättigt haben. In diesem Fall haben Sie ohnehin größere Probleme. Ich würde wahrscheinlich empfehlen, für jeden Kern mit Hyperthreading einen Prozess zu beginnen (im MRT).

Verwandte Themen