Stehen wir in der Warteschlange und serialisieren wir ordnungsgemäß?


13

Wir verarbeiten Nachrichten über eine Vielzahl von Diensten (eine Nachricht wird wahrscheinlich 9 Dienste berühren, bevor sie erledigt sind, von denen jeder eine bestimmte E / A-bezogene Funktion ausführt). Im Moment haben wir eine Kombination aus Worst-Case (XML-Datenvertragsserialisierung) und Best-Case (In-Memory-MSMQ) für die Leistung.

Die Art der Nachricht bedeutet, dass unsere serialisierten Daten ungefähr 12-15 Kilobyte lang sind und wir ungefähr 4 Millionen Nachrichten pro Woche verarbeiten. Permanente Nachrichten in MSMQ waren für uns zu langsam, und mit zunehmenden Datenmengen spüren wir den Druck durch MSMQs speicherabgebildete Dateien. Der Server hat 16 GB Arbeitsspeicher und wächst, nur für Warteschlangen. Die Leistung leidet auch, wenn die Speichernutzung hoch ist und der Computer mit dem Auslagern beginnt. Wir führen bereits die MSMQ-Selbstbereinigung durch.

Ich habe das Gefühl, dass wir hier einen Teil falsch machen. Ich habe versucht, RavenDB zu verwenden, um die Nachrichten beizubehalten und nur einen Bezeichner in die Warteschlange zu stellen, aber die Leistung dort war sehr langsam (bestenfalls 1000 Nachrichten pro Minute). Ich bin mir nicht sicher, ob dies auf die Verwendung der Entwicklungsversion zurückzuführen ist oder nicht, aber wir brauchen definitiv einen höheren Durchsatz [1]. Das Konzept funktionierte theoretisch sehr gut, aber die Leistung war nicht der Aufgabe gewachsen.

Das Verwendungsmuster besteht aus einem Dienst, der als Router fungiert und alle Lesevorgänge durchführt. Die anderen Dienste hängen Informationen basierend auf dem Hook ihres Drittanbieters an und leiten sie an den Router weiter. Die meisten Objekte werden 9-12 Mal berührt, obwohl ungefähr 10% gezwungen sind, in diesem System eine Weile herumzuschleifen, bis die 3. Partei angemessen reagiert. Die Dienste berücksichtigen dies derzeit und weisen ein angemessenes Schlafverhalten auf, da wir aus diesem Grund das Prioritätsfeld der Nachricht verwenden.

Meine Frage ist also, was ist ein idealer Stapel für die Nachrichtenübermittlung zwischen Computern mit diskreten LANs in einer C # / Windows-Umgebung? Normalerweise würde ich mit BinaryFormatter anstelle der XML-Serialisierung beginnen, aber das ist ein Hasenloch, wenn es besser ist, die Serialisierung in einen Dokumentenspeicher auszulagern. Daher meine Frage.

[1]: Je schneller wir Nachrichten verarbeiten, desto mehr Geld verdienen wir. Wir haben empirisch bewiesen, dass es weniger wahrscheinlich ist, dass wir mit der Verarbeitung einer Nachricht später in der Woche Geld verdienen. Während die Leistung von "1000 pro Minute" sehr schnell klingt, brauchen wir diese Zahl wirklich ab 10k / Minute. Nur weil ich Nummern in Nachrichten pro Woche gebe, heißt das nicht, dass wir eine ganze Woche Zeit haben, um diese Nachrichten zu verarbeiten.

=============== Bearbeiten:

Zusätzliche Information

Basierend auf den Kommentaren werde ich einige Klarstellungen hinzufügen:

  • Ich bin nicht sicher, ob Serialisierung unser Engpass ist. Ich habe die Anwendung einem Benchmarking unterzogen, und obwohl die Serialisierung im Heatgraph angezeigt wird, ist sie nur für etwa 2,5 bis 3% der CPU-Auslastung des Dienstes verantwortlich.

  • Ich bin hauptsächlich besorgt über die Beständigkeit unserer Nachrichten und den möglichen Missbrauch von MSMQ. Wir verwenden nicht-transaktionale, nicht persistente Nachrichten, um die Leistung in der Warteschlange aufrechtzuerhalten, und ich hätte wirklich gerne persistente Nachrichten, damit sie einen Neustart überstehen.

  • Das Hinzufügen von mehr RAM ist eine Notlösung. Die Maschine hat bereits 4 GB -> 16 GB RAM verloren und es wird immer schwieriger, sie herunterzufahren, um weitere hinzuzufügen.

  • Aufgrund des Stern-Routing-Musters der Anwendung ändert sich die Hälfte der Zeit, in der ein Objekt abgelegt und dann in eine Warteschlange verschoben wird, überhaupt nicht. Dies bietet sich erneut an (IMO), um es in einer Art Schlüsselwertspeicher an einer anderen Stelle zu speichern und einfach Nachrichtenkennungen zu übergeben.

  • Das Stern-Routing-Muster ist ein wesentlicher Bestandteil der Anwendung und ändert sich nicht. Wir können es nicht auf die Hundertfünfzigste Anwendung anwenden, da jedes Teil auf dem Weg asynchron (abrufend) arbeitet und wir das Wiederholungsverhalten an einem Ort zentralisieren möchten.

  • Die Anwendungslogik ist in C # geschrieben, die Objekte sind unveränderliche POCOs, die Zielbereitstellungsumgebung ist Windows Server 2012, und wir dürfen zusätzliche Computer hochfahren, wenn eine bestimmte Software nur unter Linux unterstützt wird.

  • Meine Ziele sind die Beibehaltung des aktuellen Durchsatzes bei gleichzeitiger Reduzierung des Speicherbedarfs und Erhöhung der Fehlertoleranz bei minimalem Kapitalaufwand.


Kommentare wurden bereinigt, da die relevanten Punkte in die Frage einbezogen wurden.
ChrisF

Es wäre sinnvoll, das dringendste Problem anzugehen, bevor Sie sich Gedanken über das Auswechseln von Warteschlangensubsystemen machen (obwohl es sich eventuell noch lohnt, dies zu tun). Die Tatsache, dass das Gedächtnis außer Kontrolle gerät, deutet darauf hin, dass es immer noch irgendwo Undichtigkeiten gibt. Was wurde (wenn überhaupt) für ein Speicherprofil erstellt?
Dan Lyons

@DanLyons: Das einzige Speicherwachstum ist in MSMQ. Niemand spricht wirklich darüber, aber es scheint an nicht persistenten Nachrichten zu liegen, die alle im Speicher abgelegt sind. Da wir eine Menge Daten serialisieren, bleibt eine erhebliche Menge an Speicher zugewiesen. Der Speicher wird (eventuell) zurückgefordert, wenn die Nachrichten verbraucht sind und die interne Bereinigung von MSMQ ausgeführt wird.
Bryan Boettcher

Antworten:



4

Wir hatten vor einigen Jahren eine ähnliche Situation mit einem Nachrichtensystem in der Warteschlange (in unserem Fall Audio-Fingerabdrücke). Wir schätzten die Beständigkeit der in der Warteschlange befindlichen Datenpakete sehr, fanden jedoch heraus, dass das Einreihen aller Daten in die Festplatte und das Verbrauchen der Warteschlange von der Festplatte sehr teuer war.

Wenn wir auf speicherbasierte Warteschlangen umgestellt haben, war die Leistung außergewöhnlich, aber wir hatten ein großes Problem. Hin und wieder waren die Konsumenten der Warteschlangen längere Zeit nicht mehr erreichbar (die Konsumenten- und Produzententeile in unserem Fall sind über WAN verbunden), sodass die Warteschlange des Produzenten so weit wuchs, dass sie nicht mehr verwaltet werden konnte und wie in Ihrem Fall. Sobald der Speicherverbrauch sehr hoch war, führte ein übermäßiger Speicherverbrauch während des Austauschs zu einer vollständigen Durchforstung des Systems.

Wir haben eine Warteschlange entworfen, die wir getauft haben VMQueue (für Virtual Memory Queue, im Nachhinein ein sehr schlechter Name). Die Idee dieser Warteschlange ist, dass, wenn der Konsumentenprozess auf Par läuft, dh schnell genug verarbeitet wird, um die Anzahl der in die Warteschlange eingereihten Elemente unter einem bestimmten Niveau zu halten, er im Grunde die gleiche Leistung wie ein Speicher hat. Warteschlange. Wenn der Consumer jedoch langsamer wird oder nicht mehr verfügbar ist und die Producer-Warteschlange auf eine bestimmte Größe anwächst, beginnt die Warteschlange automatisch mit dem Paging von Elementen auf und von der Festplatte (unter Verwendung vonBinaryFormatterSerialisierung übrigens). Durch diesen Vorgang wird die Speichernutzung vollständig gesteuert, und der Paging-Vorgang ist schnell oder zumindest viel schneller als das Auslagern des virtuellen Speichers bei hoher Speicherauslastung. Sobald es dem Verbraucher gelungen ist, die Warteschlange unter den Schwellenwert zu entleeren, wird die Arbeit als reine speicherbasierte Warteschlange fortgesetzt

Wenn das System abstürzt oder neu startet, kann die Warteschlange alle ausgelagerten Elemente wiederherstellen, die auf der Festplatte gespeichert waren. Dabei gehen nur die Elemente verloren, die vor dem Absturz noch im Arbeitsspeicher gespeichert waren. Wenn Sie es sich leisten können, während eines Absturzes oder Neustarts eine begrenzte Anzahl von Paketen zu verlieren, ist diese Warteschlange möglicherweise hilfreich.

Wenn Sie interessiert sind, kann ich den VMQueueQuellcode der Klasse weitergeben, damit Sie damit herumspielen können. Die Warteschlange akzeptiert alle Klassen, die als serialisierbar markiert sind. Bei der Erstellung der Warteschlange legen Sie die Größe der Seite in Anzahl der Elemente fest. Die Klassenschnittstelle ist praktisch dieselbe wie eine Standardklasse für Warteschlangen. Der Code ist jedoch sehr alt (.net 1.1), so dass leider keine generische Schnittstelle existiert.

Ich weiß, dass der Wechsel von der bewährten MSMQ-Technologie eine große Wette ist, aber diese Warteschlange funktioniert seit fast 6 Jahren zuverlässig und hat es uns ermöglicht, Szenarien zu überleben und uns von Szenarien zu erholen, in denen die Producer-Maschine mehrere Wochen lang offline war! Bitte lassen Sie mich wissen, wenn Sie interessiert sind. :)


1

Das HP ProLiant ML350G5-System erzielt 82.000 Transaktionen pro Minute - dh es hat über das 8-fache des von Ihnen genannten Durchsatzes von "10.000 / Minute".

Leistung: 82.774 tpmC

Außerdem, um ehrlich zu sein, hätte ich gerade 64 oder sogar 128 GB RAM verwendet - RAM ist billig. Greenspun weist auf den Unterschied zwischen "RAM drauf werfen" und "einen klugen MIT-gebildeten Kerl dazu bringen, es zu optimieren" hin und der RAM gewinnt.

Am Ende hatte er einen SQL Server-Computer mit 64 GB RAM und eine Handvoll Front-End-Computer, auf denen ASP.NET-Seiten ausgeführt werden. Ohne Schwierigkeiten...

Hinweis: "Der Computer hat bereits 16 GB RAM" ist alles andere als ausreichend. In einem Artikel wurde auf einen Server verwiesen, der 400.000 Benutzer mit 64 GB RAM bewältigte.

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.