Freigeben über


Skalierbarkeit

Der Begriff Skalierbarkeit wird häufig missbraucht. Für diesen Abschnitt wird eine duale Definition bereitgestellt:

  • Skalierbarkeit ist die Möglichkeit, die verfügbaren Verarbeitungsleistung auf einem Multiprozessorsystem vollständig zu nutzen (2, 4, 8, 32 oder mehr Prozessoren).
  • Skalierbarkeit ist die Möglichkeit, eine große Anzahl von Clients zu bedienen.

Diese beiden verwandten Definitionen werden häufig als Skalierung nach obenbezeichnet. Das Ende dieses Themas enthält Tipps zum Skalieren von.

Diese Diskussion konzentriert sich ausschließlich auf das Schreiben skalierbarer Server, nicht skalierbarer Clients, da skalierbare Server häufigere Anforderungen sind. In diesem Abschnitt wird auch die Skalierbarkeit nur im Kontext von RPC- und RPC-Servern behandelt. Bewährte Methoden zur Skalierbarkeit, z. B. Reduzieren der Belastbarkeit, Vermeiden häufiger Cachefehler an globalen Speicherspeicherorten oder Vermeiden einer falschen Freigabe, werden hier nicht erläutert.

RPC-Threadingmodell

Wenn ein RPC-Aufruf von einem Server empfangen wird, wird die Serverroutine (Managerroutine) für einen von RPC bereitgestellten Thread aufgerufen. RPC verwendet einen adaptiven Threadpool, der sich erhöht und verringert, wenn die Arbeitsauslastung schwankt. Ab Windows 2000 ist der Kern des RPC-Threadpools ein Abschlussport. Der Abschlussport und die Verwendung durch RPC werden auf Null- bis niedrige Inhaltsserverroutinen abgestimmt. Dies bedeutet, dass der RPC-Threadpool die Anzahl der Wartungsthreads aggressiv erhöht, wenn einige blockiert werden. Es wird davon ausgegangen, dass das Blockieren selten ist und wenn ein Thread blockiert wird, ist dies eine temporäre Bedingung, die schnell aufgelöst wird. Dieser Ansatz ermöglicht effizienzarme Server. Ein RPC-Server, der auf einem 550MHz-Server mit acht Prozessorn ausgeführt wird, auf den über ein Hochgeschwindigkeits-Systembereichsnetzwerk (SAN) zugegriffen wird, bietet z. B. über 30.000 ungültige Anrufe pro Sekunde von über 200 Remoteclients. Dies stellt mehr als 108 Millionen Anrufe pro Stunde dar.

Das Ergebnis ist, dass der aggressive Threadpool tatsächlich in die Art und Weise gelangt, wenn der Inhalt auf dem Server hoch ist. Stellen Sie sich zur Veranschaulichung einen schwerlastigen Server vor, der für den Remotezugriff auf Dateien verwendet wird. Gehen Sie davon aus, dass der Server den einfachsten Ansatz einnimmt: Er liest/schreibt die Datei einfach synchron im Thread, in dem rpc die Serverroutine aufruft. Gehen Sie außerdem davon aus, dass wir über einen Server mit vier Prozessorn verfügen, der viele Clients bedient.

Der Server beginnt mit fünf Threads (dies variiert tatsächlich, aber fünf Threads werden zur Einfachheit verwendet). Sobald RPC den ersten RPC-Aufruf abruft, wird der Aufruf an die Serverroutine verteilt, und die Serverroutine gibt die E/A aus. Selten fehlt der Dateicache und blockiert dann das Warten auf das Ergebnis. Sobald er blockiert wird, wird der fünfte Thread freigegeben, um eine Anforderung aufzunehmen, und ein sechster Thread wird als Hot Standby erstellt. Wenn jeder zehnte E/A-Vorgang den Cache verfehlt und 100 Millisekunden blockiert (ein beliebiger Zeitwert), und vorausgesetzt, der Vierprozessorserver erfüllt ca. 20.000 Aufrufe pro Sekunde (5.000 Aufrufe pro Prozessor), würde eine vereinfachte Modellierung voraussagen, dass jeder Prozessor ca. 50 Threads liefert. Dabei wird davon ausgegangen, dass ein Aufruf, der alle 2 Millisekunden blockiert wird, und nach 100 Millisekunden wird der erste Thread wieder freigegeben, sodass der Pool bei etwa 200 Threads (50 pro Prozessor) stabilisiert wird.

Das tatsächliche Verhalten ist komplizierter, da die hohe Anzahl von Threads zusätzliche Kontextwechsel verursacht, die den Server verlangsamen und auch die Rate der Erstellung neuer Threads verlangsamen, aber die Grundidee ist klar. Die Anzahl der Threads geht schnell hoch, wenn Threads auf dem Server mit dem Blockieren und Warten auf etwas beginnen (sei es eine E/A oder zugriff auf eine Ressource).

RPC und der Abschlussport, der eingehende Anforderungen sendet, versuchen, die Anzahl der verwendbaren RPC-Threads auf dem Server beizubehalten, die der Anzahl der Prozessoren auf dem Computer entsprechen. Dies bedeutet, dass auf einem Vierprozessorserver, sobald ein Thread zu RPC zurückkehrt, wenn vier oder mehr verwendbare RPC-Threads vorhanden sind, der fünfte Thread keine neue Anforderung aufnehmen darf und stattdessen in einem Hot Standby-Zustand für den Fall, dass einer der derzeit verwendbaren Threads blockiert wird. Wenn der fünfte Thread lange genug als Hot Standby wartet, ohne dass die Anzahl der verwendbaren RPC-Threads unter der Anzahl der Prozessoren fällt, wird er freigegeben, d. h. der Threadpool wird verringert.

Stellen Sie sich einen Server mit vielen Threads vor. Wie zuvor erläutert, endet ein RPC-Server mit vielen Threads, aber nur, wenn die Threads häufig blockieren. Auf einem Server, auf dem Threads häufig blockieren, wird ein Thread, der zu RPC zurückkehrt, bald aus der Hot-Standby-Liste genommen, da alle derzeit verwendbaren Threads blockieren und eine Anforderung zum Verarbeiten erhalten. Wenn ein Thread blockiert wird, wechselt der Thread dispatcher im Kernelkontext zu einem anderen Thread. Dieser Kontextwechsel verbraucht selbst CPU-Zyklen. Der nächste Thread führt unterschiedliche Code aus, greift auf verschiedene Datenstrukturen zu und verfügt über einen anderen Stapel, was bedeutet, dass die Speichercachetreffrate (die L1- und L2-Caches) viel niedriger ist, was zu einer langsameren Ausführung führt. Die zahlreichen Threads, die gleichzeitig ausgeführt werden, erhöhen den Inhalt für vorhandene Ressourcen, z. B. Heap, kritische Abschnitte im Servercode usw. Dies erhöht die Konvoys als Konvoys in der Ressourcenform. Wenn der Arbeitsspeicher niedrig ist, führt der von der großen und wachsenden Anzahl von Threads ausgeübte Speicherdruck zu Seitenfehlern, wodurch die Rate, mit der die Threads blockiert werden, weiter erhöht und noch mehr Threads erstellt werden. Je nachdem, wie oft der physische Arbeitsspeicher blockiert wird und wie viel physischer Arbeitsspeicher verfügbar ist, kann der Server entweder mit einer hohen Kontextumschaltungsrate stabilisiert werden, oder es kann sich zu dem Punkt verschlechtern, an dem er nur wiederholt auf die Festplatte und den Kontextwechsel zugreift, ohne dass eine tatsächliche Arbeit ausgeführt wird. Diese Situation wird natürlich nicht unter leichter Arbeitsauslastung angezeigt, aber eine hohe Arbeitsauslastung bringt das Problem schnell auf die Oberfläche.

Wie kann dies verhindert werden? Wenn Threads blockiert werden sollen, deklarieren Sie Aufrufe als asynchron, und nachdem die Anforderung die Serverroutine eingibt, in eine Warteschlange in einen Pool von Arbeitsthreads, die die asynchronen Funktionen des E/A-Systems und/oder RPC verwenden. Wenn der Server wiederum RPC-Aufrufe ausführt, führen diese asynchron aus, und stellen Sie sicher, dass die Warteschlange nicht zu groß wird. Wenn die Serverroutine Datei-E/A ausführt, verwenden Sie asynchrone Datei-E/A, um mehrere Anforderungen an das E/A-System in die Warteschlange zu stellen und nur einige Threads in die Warteschlange zu stellen und die Ergebnisse aufzunehmen. Wenn die Serverroutine erneut Netzwerk-E/A ausführt, verwenden Sie die asynchronen Funktionen des Systems, um die Anforderungen auszugeben und die Antworten asynchron aufzunehmen und so wenige Threads wie möglich zu verwenden. Wenn die E/A abgeschlossen ist oder der vom Server getätigte RPC-Aufruf abgeschlossen ist, schließen Sie den asynchronen RPC-Aufruf ab, der die Anforderung übermittelt hat. Dadurch kann der Server mit so wenigen Threads wie möglich ausgeführt werden, was die Leistung erhöht und die Anzahl der Clients, die ein Server verwenden kann.

Skalieren

RPC kann für die Arbeit mit netzwerklastenausgleich (Network Load Balancing, NLB) konfiguriert werden, wenn NLB so konfiguriert ist, dass alle Anforderungen einer bestimmten Clientadresse auf denselben Server gehen. Da jeder RPC-Client einen Verbindungspool öffnet (weitere Informationen finden Sie unter RPC und), ist es wichtig, dass alle Verbindungen vom Pool des angegebenen Clients auf demselben Servercomputer enden. Solange diese Bedingung erfüllt ist, kann ein NLB-Cluster so konfiguriert werden, dass er als einen großen RPC-Server mit potenziell hervorragender Skalierbarkeit funktioniert.