Der Tag, an dem Dodo aufgehört hat. Asynchrones Skript

Hallo Habr! Jeder SRE in unserem Team träumte einmal davon, nachts friedlich zu schlafen. Träume werden wahr. In diesem Artikel werde ich darüber sprechen und wie wir die Leistung und Stabilität unseres Dodo IS-Systems erreichen.


Eine Reihe von Artikeln über den Zusammenbruch des Dodo IS * -Systems :

1. Der Tag, an dem Dodo aufgehört hat. Synchrones Skript.
2. Der Tag, an dem Dodo aufgehört hat. Asynchrones Skript.

* Die Materialien wurden basierend auf meiner Leistung bei DotNext 2018 in Moskau geschrieben .
In einem früheren Artikel haben wir uns mit dem Blockieren von Codeproblemen im Preemptive Multitasking-Paradigma befasst. Es wurde angenommen, dass es notwendig war, den Blockierungscode auf async / await neu zu schreiben. Also haben wir es getan. Lassen Sie uns nun darüber sprechen, welche Probleme dabei aufgetreten sind.

Wir führen den Begriff Parallelität ein


Bevor Sie zu Async gelangen, müssen Sie den Begriff Parallelität eingeben.
In der Warteschlangentheorie ist Parallelität die Anzahl der Clients, die sich derzeit im System befinden. Parallelität wird manchmal mit Parallelität verwechselt, aber in Wirklichkeit sind dies zwei verschiedene Dinge.
Für diejenigen, die zum ersten Mal neu bei Concurrency sind, empfehle ich Rob Pikes Video . Parallelität ist, wenn wir viele Dinge gleichzeitig erledigen, und Parallelität ist, wenn wir viele Dinge gleichzeitig tun.

In Computern passieren nicht viele Dinge parallel. Eine solche Sache ist das Rechnen auf mehreren Prozessoren. Der Grad der Parallelität wird durch die Anzahl der CPU-Threads begrenzt.

Tatsächlich ist Threads Teil des Preemptive Multitasking-Konzepts, einer Möglichkeit, die Parallelität in einem Programm zu modellieren, wenn wir uns in der Frage der Parallelität auf das Betriebssystem verlassen. Dieses Modell bleibt nützlich, solange wir verstehen, dass wir uns speziell mit dem Parallelitätsmodell und nicht mit der Parallelität befassen.

Async / await ist syntaktischer Zucker für State Machine, ein weiteres nützliches Parallelitätsmodell, das in einer Single-Thread-Umgebung ausgeführt werden kann. Im Wesentlichen handelt es sich um kooperatives Multitasking - das Modell selbst berücksichtigt die Parallelität überhaupt nicht. In Kombination mit Multithreading erhalten wir ein Modell über das andere, und das Leben ist sehr kompliziert.

Vergleich der beiden Modelle


Wie es im Preemptive Multitasking-Modell funktioniert hat


Angenommen, wir haben 20 Threads und 20 Anforderungen in der Verarbeitung pro Sekunde. Das Bild zeigt einen Spitzenwert - 200 Anforderungen gleichzeitig im System. Wie konnte das passieren:

  • Anforderungen können gruppiert werden, wenn 200 Clients gleichzeitig auf eine Schaltfläche klicken.
  • Der Garbage Collector konnte Anforderungen für einige zehn Millisekunden stoppen.
  • Anforderungen können in jeder Warteschlange verzögert werden, wenn der Proxy die Warteschlange unterstützt.

Es gibt viele Gründe, warum sich Anfragen für einen kurzen Zeitraum angesammelt haben und in einem einzigen Bündel eingehen. Auf jeden Fall passierte nichts Schreckliches, sie standen in der Thread Pool Warteschlange und wurden langsam fertig. Es gibt keine Gipfel mehr, alles geht weiter, als wäre nichts passiert.

Angenommen, der Smart Thread Pool-Algorithmus (und es gibt dort Elemente des maschinellen Lernens) hat entschieden, dass es bisher keinen Grund gibt, die Anzahl der Threads zu erhöhen. Der Verbindungspool in MySql ist ebenfalls 20, da Threads = 20 ist. Dementsprechend benötigen wir nur 20 Verbindungen zu SQL.



In diesem Fall ist die Parallelitätsstufe des Servers aus Sicht des externen Systems = 200. Der Server hat diese Anforderungen bereits empfangen, aber noch nicht abgeschlossen. Für eine Anwendung, die im Multithreading-Paradigma ausgeführt wird, ist die Anzahl gleichzeitiger Anforderungen jedoch durch die aktuelle Größe des Thread-Pools = 20 begrenzt. Es handelt sich also um den Grad der Parallelität = 20.

Wie jetzt alles im asynchronen Modell funktioniert




Mal sehen, was in einer Anwendung passiert, die async / await mit derselben Last und Verteilung von Anforderungen ausführt. Vor dem Erstellen einer Aufgabe gibt es keine Warteschlange, und die Anforderung wird sofort verarbeitet. Natürlich wird Thread von ThreadPool für kurze Zeit verwendet, und der erste Teil der Anforderung wird sofort ausgeführt, bevor die Datenbank kontaktiert wird. Da Thread schnell zum Thread-Pool zurückkehrt, benötigen wir nicht viele Threads zur Verarbeitung. In diesem Diagramm wird der Thread-Pool überhaupt nicht angezeigt, er ist transparent.



Was bedeutet das für unsere Bewerbung? Das externe Bild ist das gleiche - der Grad der Parallelität = 200. Gleichzeitig hat sich die Situation im Inneren geändert. Früher waren Anforderungen in der ThreadPool-Warteschlange "überfüllt", jetzt beträgt der Grad der Parallelität der Anwendung ebenfalls 200, da TaskScheduler keine Einschränkungen aufweist. Hurra! Wir haben das Ziel der Asynchronität erreicht - die Anwendung "bewältigt" nahezu jeden Grad an Parallelität!

Folgen: nichtlineare Verschlechterung des Systems


Die Anwendung ist aus Sicht der Parallelität transparent geworden, sodass die Parallelität jetzt auf die Datenbank projiziert wird. Jetzt brauchen wir einen Verbindungspool mit der gleichen Größe = 200. Die Datenbank ist die CPU, der Speicher, das Netzwerk, der Speicher. Dies ist der gleiche Dienst mit seinen Problemen wie jeder andere. Je mehr Anforderungen wir gleichzeitig ausführen möchten, desto langsamer werden sie ausgeführt.

Bei voller Auslastung der Datenbank verschlechtert sich die Antwortzeit bestenfalls linear: Sie haben doppelt so viele Abfragen gestellt, sie begann doppelt so langsam zu arbeiten. In der Praxis tritt aufgrund des Abfragewettbewerbs zwangsläufig ein Overhead auf, und es kann sich herausstellen, dass sich das System nicht linear verschlechtert.

Warum passiert das?


Gründe für die zweite Bestellung:

  • Jetzt muss die Datenbank gleichzeitig im Speicher der Datenstruktur gespeichert werden, um mehr Anforderungen zu bedienen.
  • Jetzt muss die Datenbank größere Sammlungen bedienen (und dies ist algorithmisch nachteilig).

Grund erster Ordnung:


Am Ende kämpft Async gegen begrenzte Ressourcen und ... gewinnt! Die Datenbank schlägt fehl und wird langsamer. Dadurch erhöht der Server die Parallelität weiter und das System kann diese Situation nicht mehr mit Ehre verlassen.

Server Sudden Death Syndrom


Manchmal tritt eine interessante Situation auf. Wir haben einen Server. Er arbeitet so für sich, alles ist in Ordnung. Es gibt genügend Ressourcen, auch mit einem Spielraum. Dann erhalten wir plötzlich eine Nachricht von Clients, dass der Server langsamer wird. Wir sehen uns das Diagramm an und stellen fest, dass die Kundenaktivität etwas zugenommen hat, aber jetzt ist alles normal. Denken Sie an einen DOS-Angriff oder Zufall. Jetzt scheint alles in Ordnung zu sein. Erst jetzt ist der Server weiterhin dumm und alles ist härter, bis Zeitüberschreitungen auftreten. Nach einiger Zeit beginnt sich auch ein anderer Server zu verbiegen, der dieselbe Datenbank verwendet. Eine vertraute Situation?

Warum ist das System gestorben?


Sie können versuchen, dies dadurch zu erklären, dass der Server irgendwann eine Spitzenanzahl von Anforderungen erhalten hat und „pleite“ ist. Wir wissen jedoch, dass die Last reduziert wurde und der Server danach lange Zeit nicht besser wurde, bis die Last vollständig verschwunden war.

Die rhetorische Frage: Sollte der Server wegen übermäßiger Auslastung kaputt gehen? Tun sie das?

Wir simulieren eine Serverabsturzsituation


Hier werden keine Grafiken aus einem realen Produktionssystem analysiert. Wenn der Server abstürzt, können wir oft keinen solchen Zeitplan erhalten. Dem Server gehen die CPU-Ressourcen aus, und daher kann er keine Protokolle schreiben und keine Metriken angeben. In den Diagrammen zum Zeitpunkt der Katastrophe wird häufig ein Bruch in allen Diagrammen beobachtet.

SREs sollten in der Lage sein, Überwachungssysteme herzustellen, die für diesen Effekt weniger anfällig sind. Systeme, die in jeder Situation zumindest einige Informationen liefern und gleichzeitig Post-Mortem-Systeme mithilfe fragmentarischer Informationen analysieren können. Für Bildungszwecke verwenden wir in diesem Artikel einen etwas anderen Ansatz.

Versuchen wir, ein Modell zu erstellen, das mathematisch wie ein Server unter Last funktioniert. Als nächstes werden wir die Eigenschaften des Servers untersuchen. Wir verwerfen die Nichtlinearität realer Server und simulieren eine Situation, in der eine lineare Verzögerung auftritt, wenn die Last über den Nennwert steigt. Doppelt so viele Anfragen wie nötig - wir bedienen doppelt so langsam.

Dieser Ansatz ermöglicht:

  • Überlegen Sie, was am besten passieren wird.
  • Nehmen Sie genaue Metriken.

Geplante Navigation:

  • blau - die Anzahl der Anfragen an den Server;
  • grün - Serverantworten;
  • gelb - Timeouts;
  • dunkelgrau - Anforderungen, die an Serverressourcen gesendet wurden, weil der Client nicht auf eine Timeout-Antwort gewartet hat. Manchmal kann ein Client dies dem Server durch eine Trennung melden, aber im Allgemeinen ist ein solcher Luxus technisch möglicherweise nicht realisierbar, beispielsweise wenn der Server CPU-gebundene Arbeit ohne Zusammenarbeit mit dem Client ausführt.




Warum stellte sich heraus, dass das Anforderungsdiagramm des Kunden (blau im Diagramm) so war? In der Regel wächst der Bestellplan in unseren Pizzerien morgens reibungslos und nimmt abends ab. Wir beobachten jedoch drei Peaks vor dem Hintergrund der üblichen gleichmäßigen Kurve. Diese Form des Diagramms wurde nicht zufällig für das Modell ausgewählt, sondern. Das Modell wurde während der Untersuchung eines realen Vorfalls mit dem Server des Pizzeria Contact Centers in Russland während der Weltmeisterschaft geboren.

Fall "Weltmeisterschaft"


Wir saßen und warteten auf weitere Bestellungen. Vorbereitet für die Meisterschaft können die Server nun einen Festigkeitstest bestehen.

Der erste Höhepunkt - Fußballfans schauen sich die Meisterschaft an, sie haben Hunger und kaufen Pizza. Während der ersten Hälfte sind sie beschäftigt und können nicht bestellen. Aber Menschen, denen der Fußball gleichgültig ist, können, also geht auf der Karte alles wie gewohnt weiter.

Und dann endet die erste Hälfte und der zweite Höhepunkt kommt. Die Fans wurden nervös, hungrig und machten dreimal mehr Bestellungen als auf dem ersten Gipfel. Pizza wird zu einem schrecklichen Preis gekauft. Dann beginnt die zweite Hälfte und wieder keine Pizza.

In der Zwischenzeit beginnt sich der Contact Center-Server langsam zu biegen und Anforderungen immer langsamer zu bearbeiten. Die Systemkomponente, in diesem Fall der Call Center-Webserver, ist destabilisiert.

Der dritte Höhepunkt wird kommen, wenn das Spiel vorbei ist. Fans und das System warten auf eine Strafe.

Wir analysieren die Gründe für den Serverabsturz


Was ist passiert? Der Server kann 100 bedingte Anforderungen enthalten. Wir verstehen, dass es für diese Kraft ausgelegt ist und es nicht mehr aushält. Es kommt ein Gipfel, der an sich nicht so groß ist. Die Grauzone der Parallelität ist jedoch viel höher.

Das Modell ist so konzipiert, dass die Parallelität numerisch der Anzahl der Bestellungen pro Sekunde entspricht. In der Grafik sollte sie also visuell den gleichen Maßstab haben. Es ist jedoch viel höher, weil es sich ansammelt.

Wir sehen hier einen Schatten aus dem Diagramm - dies sind Anforderungen, die ausgeführt wurden und ausgeführt wurden (angezeigt durch den ersten roten Pfeil). Die Zeitskala ist abhängig vom Zeitversatz. Der zweite Peak hat unseren Server bereits ausgeschaltet. Er stürzte ab und begann viermal weniger Anfragen als gewöhnlich zu bearbeiten.



In der zweiten Hälfte des Diagramms ist klar, dass einige Anforderungen zunächst noch ausgeführt wurden, dann aber gelbe Flecken auftraten - die Anforderungen wurden vollständig gestoppt.



Noch einmal den ganzen Zeitplan. Es ist zu sehen, dass die Parallelität wild wird. Ein riesiger Berg erscheint.



Normalerweise haben wir völlig unterschiedliche Metriken analysiert: Wie langsam wurde die Anfrage abgeschlossen, wie viele Anfragen pro Sekunde. Wir haben uns nicht einmal die Parallelität angesehen, wir haben nicht einmal über diese Metrik nachgedacht. Aber vergebens, denn genau diese Menge zeigt den Moment des Serverausfalls am besten.

Aber woher kam so ein riesiger Berg? Die größte Lastspitze ist längst vorbei!

Kleines Gesetz


Das Gesetz von Little regelt die Parallelität.

L (Anzahl der Kunden im System) = λ (Geschwindigkeit ihres Aufenthalts) ∗ W (Zeit, die sie im System verbringen)

Dies ist ein Durchschnitt. Unsere Situation entwickelt sich jedoch dramatisch, der Durchschnitt passt nicht zu uns. Wir werden diese Gleichung differenzieren und dann integrieren. Schauen Sie sich dazu das Buch von John Little an, der diese Formel erfunden hat, und sehen Sie dort das Integral.



Wir haben die Anzahl der Einträge im System und die Anzahl derer, die das System verlassen. Die Anfrage kommt und geht, wenn alles abgeschlossen ist. Unten sehen Sie eine grafische Wachstumsregion, die dem linearen Wachstum der Parallelität entspricht.



Es gibt nur wenige grüne Anfragen. Dies sind diejenigen, die tatsächlich implementiert werden. Die blauen sind diejenigen, die kommen. Zwischen den Zeiten haben wir die übliche Anzahl von Anfragen, die Situation ist stabil. Aber die Parallelität wächst weiter. Der Server wird diese Situation selbst nicht mehr bewältigen. Dies bedeutet, dass er bald fallen wird.

Aber warum nimmt die Parallelität zu? Wir betrachten das Integral der Konstanten. In unserem System ändert sich nichts, aber das Integral sieht aus wie eine lineare Funktion, die nur erwachsen wird.

Werden wir spielen?


Die Erklärung mit Integralen ist kompliziert, wenn Sie sich nicht an Mathematik erinnern. Hier schlage ich vor, mich aufzuwärmen und das Spiel zu spielen.

Spiel Nummer 1


Voraussetzungen : Der Server empfängt Anforderungen, die jeweils drei Verarbeitungsperioden auf der CPU erfordern. Die CPU-Ressource wird gleichmäßig auf alle Aufgaben aufgeteilt. Dies ähnelt dem Verbrauch von CPU-Ressourcen während des präemptiven Multitasking. Die Zahl in der Zelle gibt an, wie viel Arbeit nach dieser Maßnahme noch übrig ist. Für jeden bedingten Schritt kommt eine neue Anforderung an.

Stellen Sie sich vor, Sie haben eine Anfrage erhalten. Nur 3 Arbeitseinheiten, am Ende der ersten Verarbeitungsperiode verbleiben 2 Einheiten.

In der zweiten Periode wird eine weitere Anforderung geschichtet, jetzt sind beide CPUs beschäftigt. Sie haben eine Arbeitseinheit für die ersten beiden Abfragen ausgeführt. Es bleiben 1 und 2 Einheiten für die erste bzw. zweite Anforderung zu vervollständigen.

Jetzt ist die dritte Anfrage gekommen und der Spaß beginnt. Es scheint, dass die erste Anforderung hätte abgeschlossen werden müssen, aber in diesem Zeitraum teilen sich bereits drei Anforderungen die CPU-Ressource, sodass der Abschlussgrad für alle drei Anforderungen am Ende des dritten Verarbeitungszeitraums jetzt gebrochen ist:



Noch interessanter! Die vierte Anforderung wird hinzugefügt, und jetzt beträgt der Grad der Parallelität bereits 4, da für alle vier Anforderungen in diesem Zeitraum eine Ressource erforderlich war. In der Zwischenzeit ist die erste Anforderung bis zum Ende der vierten Periode bereits abgeschlossen, sie geht nicht zur nächsten Periode über und es sind noch 0 Jobs für die CPU übrig.

Da die erste Anfrage bereits abgeschlossen ist, lassen Sie uns für ihn zusammenfassen: Sie lief ein Drittel länger als erwartet. Es wurde angenommen, dass die Länge jeder Aufgabe horizontal idealerweise = 3 ist, je nach Arbeitsaufwand. Wir markieren es mit Orange, als Zeichen dafür, dass wir mit dem Ergebnis nicht ganz zufrieden sind.



Die fünfte Anfrage kommt an. Der Grad der Parallelität beträgt immer noch 4, aber wir sehen, dass in der fünften Spalte die verbleibende Arbeit insgesamt mehr ist. Dies liegt daran, dass in der vierten Spalte mehr Arbeit übrig ist als in der dritten.

Wir fahren mit drei weiteren Perioden fort. Warten auf Antworten.
- Server, hallo!
- ...



"Ihr Anruf ist uns sehr wichtig ..."



Nun, endlich kam die Antwort auf die zweite Anfrage. Die Reaktionszeiten sind doppelt so lang wie erwartet.



Der Grad der Parallelität hat sich bereits verdreifacht, und nichts deutet darauf hin, dass sich die Situation zum Besseren ändern wird. Ich habe nicht weiter gezeichnet, da die Antwortzeit auf die dritte Anfrage nicht mehr ins Bild passt.

Unser Server ist in einen unerwünschten Zustand eingetreten, aus dem er niemals alleine austritt. Spiel vorbei

Was kennzeichnet den GameOver-Status des Servers?


Anforderungen werden auf unbestimmte Zeit im Speicher gespeichert. Früher oder später wird die Erinnerung einfach enden. Darüber hinaus erhöht sich mit zunehmender Skalierung der CPU-Overhead für die Wartung verschiedener Datenstrukturen. Beispielsweise sollte der Verbindungspool jetzt Zeitüberschreitungen für weitere Verbindungen verfolgen, der Garbage Collector sollte jetzt mehr Objekte auf dem Heap überprüfen und so weiter.

Die Untersuchung aller möglichen Konsequenzen der Anhäufung aktiver Objekte ist nicht das Ziel dieses Artikels, aber selbst eine einfache Anhäufung von Daten im RAM reicht bereits aus, um den Server zu füllen. Darüber hinaus haben wir bereits festgestellt, dass der Client-Server seine Parallelitätsprobleme auf den Datenbankserver und andere Server projiziert, die er als Client verwendet.

Das Interessanteste: Selbst wenn Sie jetzt eine geringere Last an den Server senden, wird dieser immer noch nicht wiederhergestellt. Alle Anforderungen enden mit einem Timeout und der Server verbraucht alle verfügbaren Ressourcen.

Und was haben wir eigentlich erwartet ?! Schließlich haben wir dem Server wissentlich eine Menge Arbeit gegeben, die er nicht bewältigen konnte.

Wenn Sie sich mit verteilter Systemarchitektur befassen, ist es hilfreich darüber nachzudenken, wie normale Menschen solche Probleme lösen. Nehmen Sie zum Beispiel einen Nachtclub. Es funktioniert nicht mehr, wenn zu viele Personen es betreten. Der Türsteher bewältigt das Problem einfach: Es sieht so aus, wie viele Menschen sich darin befinden. Einer links - startet einen anderen. Ein neuer Gast wird kommen und die Größe der Warteschlange schätzen. Wenn die Schlange lang ist, wird er nach Hause gehen. Was ist, wenn Sie diesen Algorithmus auf den Server anwenden?



Lass uns nochmal spielen.

Spiel Nummer 2


Voraussetzungen : Wir haben wieder zwei CPUs, die gleichen Aufgaben von 3 Einheiten, die in jeder Periode eintreffen, aber jetzt werden wir den Türsteher einstellen und die Aufgaben werden klug sein - wenn sie sehen, dass die Warteschlange 2 ist, gehen sie sofort nach Hause.





Die dritte Anfrage kam. In dieser Zeit steht er in der Schlange. Er hat die Nummer 3 am Ende des Zeitraums. Die Residuen enthalten keine Bruchzahlen, da zwei CPUs zwei Aufgaben ausführen, eine für einen bestimmten Zeitraum.

Obwohl wir drei überlagerte Anforderungen haben, ist der Grad der Parallelität innerhalb des Systems = 2. Der dritte befindet sich in der Warteschlange und zählt nicht.



Der vierte kam - das gleiche Bild, obwohl bereits mehr Arbeit angesammelt wurde.


...
...

In der sechsten Periode wurde die dritte Anforderung mit einer dritten Verzögerung abgeschlossen, und der Grad der Parallelität beträgt bereits = 4.



Der Grad der Parallelität hat sich verdoppelt. Sie kann nicht mehr wachsen, weil wir dies eindeutig verboten haben. Mit maximaler Geschwindigkeit wurden nur die ersten beiden Anfragen erfüllt - diejenigen, die zuerst in den Club kamen, während genügend Platz für alle vorhanden war.

Die gelben Anforderungen waren länger im System, standen jedoch in einer Reihe und verzögerten die CPU-Ressource nicht. Deshalb hatten diejenigen, die drinnen waren, Spaß. Dies könnte so weitergehen, bis ein Mann kam und sagte, er würde nicht in der Schlange stehen, sondern nach Hause gehen. Dies ist eine fehlgeschlagene Anforderung:



Die Situation kann endlos wiederholt werden, während die Ausführungszeit der Abfrage auf dem gleichen Niveau bleibt - genau doppelt so lange, wie wir möchten.



Wir sehen, dass eine einfache Einschränkung der Parallelitätsebene das Problem der Serverlebensfähigkeit beseitigt.

So erhöhen Sie die Server-Lebensfähigkeit durch Begrenzung der Parallelität


Sie können den einfachsten "Türsteher" selbst schreiben. Unten ist der Code mit dem Semaphor. Die Länge der Leitung außerhalb ist unbegrenzt. , .

const int MaxConcurrency = 100; SemaphoreSlim bulkhead = new SemaphoreSlim(MaxConcurrency, MaxConcurrency); public async Task ProcessRequest() { if (!await bulkhead.WaitAsync()) { throw new OperationCanceledException(); } try { await ProcessRequestInternal(); return; } finally { bulkhead.Release(); } } 

Um eine begrenzte Warteschlange zu erstellen, benötigen Sie zwei Semaphoren. Hierfür eignet sich die von Microsoft empfohlene Polly-Bibliothek . Achten Sie auf das Schottmuster. Wörtlich übersetzt als "Schott" - ein Strukturelement, das es dem Schiff ermöglicht, nicht zu sinken. Um ehrlich zu sein, denke ich, dass der Begriff Türsteher besser geeignet ist. Wichtig ist, dass dieses Muster es dem Server ermöglicht, in hoffnungslosen Situationen zu überleben.

Zuerst drücken wir alles, was auf der Ladebank möglich ist, vom Server aus, bis wir bestimmen, wie viele Anforderungen es halten kann. Zum Beispiel haben wir festgestellt, dass es 100 ist. Wir setzen Schott.

Außerdem überspringt der Server nur die erforderliche Anzahl von Anforderungen, der Rest wird in die Warteschlange gestellt. Es wäre ratsam, eine etwas kleinere Zahl zu wählen, damit ein Spielraum entsteht. Ich habe keine vorgefertigte Empfehlung zu diesem Thema, da eine starke Abhängigkeit vom Kontext und der spezifischen Situation besteht.

  1. Wenn das Serververhalten in Bezug auf die Ressourcen stabil von der Auslastung abhängt, kann sich diese Anzahl dem Grenzwert nähern.
  2. Wenn das Medium Lastschwankungen ausgesetzt ist, sollte unter Berücksichtigung der Größe dieser Schwankungen eine konservativere Anzahl gewählt werden. Solche Schwankungen können aus verschiedenen Gründen auftreten, beispielsweise ist die Leistungsumgebung mit GC durch kleine Lastspitzen auf der CPU gekennzeichnet.
  3. Wenn der Server regelmäßig Aufgaben nach einem Zeitplan ausführt, sollte dies ebenfalls berücksichtigt werden. Sie können sogar ein adaptives Schott entwickeln, das berechnet, wie viele Abfragen gleichzeitig ohne Serververschlechterung gesendet werden können (dies geht jedoch bereits über den Rahmen dieser Studie hinaus).

Abfrageexperimente


Schauen Sie sich zuletzt dieses Post-Mortem an, wir werden es nicht wieder sehen.

All dieser graue Haufen korreliert eindeutig mit dem Serverabsturz. Grau ist der Tod für den Server. Lassen Sie es uns einfach abschneiden und sehen, was passiert. Es scheint, dass eine bestimmte Anzahl von Anfragen nach Hause gehen wird, einfach nicht erfüllt wird. Aber wie viel?

100 innen, 100 außen



Es stellte sich heraus, dass unser Server sehr gut und unterhaltsam zu leben begann. Er pflügt ständig mit maximaler Leistung. Wenn ein Peak auftritt, wird er natürlich rausgeschmissen, aber nicht lange.

Inspiriert vom Erfolg werden wir versuchen sicherzustellen, dass er überhaupt nicht abprallt. Versuchen wir, die Länge der Warteschlange zu erhöhen.

100 innen, 500 außen




Es wurde besser, aber der Schwanz wuchs. Dies sind die Anforderungen, die lange Zeit später ausgeführt werden.

100 innen, 1000 außen


Da etwas besser geworden ist, versuchen wir, es auf den Punkt der Absurdität zu bringen. Lösen wir die Warteschlangenlänge zehnmal länger auf, als wir gleichzeitig bedienen können:



Wenn wir über die Metapher des Clubs und der Türsteher sprechen, ist eine solche Situation kaum möglich - niemand möchte länger am Eingang warten, als Zeit im Club zu verbringen. Wir werden auch nicht so tun, als wäre dies eine normale Situation für unser System.

Es ist besser, den Kunden überhaupt nicht zu bedienen, als ihn auf der Website oder in der mobilen Anwendung zu quälen, indem Sie jeden Bildschirm 30 Sekunden lang laden und den Ruf des Unternehmens beeinträchtigen. Es ist besser, einem kleinen Teil der Kunden sofort ehrlich zu sagen, dass wir sie jetzt nicht mehr bedienen können. Andernfalls werden wir alle Kunden um ein Vielfaches langsamer bedienen, da die Grafik zeigt, dass die Situation noch einige Zeit anhält.

Es gibt noch ein weiteres Risiko: Andere Systemkomponenten sind möglicherweise nicht für ein solches Serververhalten ausgelegt, und wie wir bereits wissen, wird Parallelität auf Clients projiziert.

Daher kehren wir zur ersten Option „100 pro 100“ zurück und überlegen, wie wir unsere Kapazitäten skalieren können.

Gewinner - 100 innen, 100 außen




¯ \ _ (ツ) _ / ¯

Mit diesen Parametern ist die größte Verschlechterung der Laufzeit genau das Zweifache des „Nennwerts“. Gleichzeitig wird die Ausführungszeit der Abfrage um 100% verkürzt.

Wenn Ihr Client empfindlich auf die Laufzeit reagiert (und dies gilt normalerweise sowohl für menschliche Clients als auch für Server-Clients), können Sie die Warteschlangenlänge weiter reduzieren. In diesem Fall können wir einen bestimmten Prozentsatz der internen Parallelität übernehmen, und wir werden sicher wissen, dass sich die Antwortzeit des Dienstes im Durchschnitt nicht um mehr als diesen Prozentsatz verschlechtert.

Tatsächlich versuchen wir nicht, eine Warteschlange zu erstellen, sondern uns vor Lastschwankungen zu schützen. Genau wie bei der Bestimmung des ersten Parameters der Trennwand (Menge im Inneren) ist es hier nützlich zu bestimmen, welche Schwankungen in der Last der Kunde verursachen kann. Wir werden also wissen, in welchen Fällen wir grob gesagt den Gewinn aus potenziellen Dienstleistungen verpassen werden.

Es ist noch wichtiger zu bestimmen, welche Latenzschwankungen anderen Systemkomponenten standhalten können, die mit dem Server interagieren. Wir werden also wissen, dass wir wirklich das Maximum aus dem vorhandenen System herausholen, ohne die Gefahr eines vollständigen Verlusts des Dienstes.

Diagnose und Behandlung


Wir behandeln unkontrollierte Parallelität mit Schottisolierung.
Diese Methode wird, wie die anderen in dieser Artikelserie beschriebenen, bequem von der Polly-Bibliothek implementiert.

Der Vorteil des Verfahrens besteht darin, dass es äußerst schwierig sein wird, eine einzelne Komponente des Systems als solche zu destabilisieren. Das System erhält ein sehr vorhersehbares zeitliches Verhalten für erfolgreiche Anforderungen und viel höhere Chancen für erfolgreiche vollständige Anforderungen.

Wir lösen jedoch nicht alle Probleme. Zum Beispiel das Problem der unzureichenden Serverleistung. In dieser Situation müssen Sie sich natürlich dafür entscheiden, den Ballast im Falle eines Lastsprungs, den wir als übermäßig eingestuft haben, fallen zu lassen.

Weitere Maßnahmen, die in unserer Studie nicht behandelt werden, können beispielsweise die dynamische Skalierung umfassen.

Source: https://habr.com/ru/post/de461081/


All Articles