Heim > Artikel > Betrieb und Instandhaltung > Warum ist die Nginx-Leistung gut?
Nachdem Nginx gestartet wurde, wird es im Hintergrund als Daemon im Unix-System ausgeführt. Der Hintergrundprozess umfasst einen Master-Prozess und mehrere Worker-Prozesse. Wir können den Hintergrundmodus auch manuell deaktivieren, Nginx im Vordergrund laufen lassen und Nginx so konfigurieren, dass der Masterprozess abgebrochen wird, sodass Nginx im Einzelprozessmodus ausgeführt werden kann.
Natürlich werden wir dies definitiv nicht in einer Produktionsumgebung tun, daher wird das Ausschalten des Hintergrundmodus im Allgemeinen zum Debuggen verwendet. In den folgenden Kapiteln erklären wir ausführlich, wie man Nginx debuggt.
Wir können also sehen, dass Nginx im Multiprozessmodus funktioniert. Natürlich unterstützt Nginx auch Multithreading. Unsere Mainstream-Methode ist jedoch immer noch die Multiprozessmethode, die auch die Standardmethode ist von Nginx. Die Verwendung von Multiprozessen in Nginx bietet viele Vorteile, daher werde ich hauptsächlich den Multiprozessmodus von Nginx erläutern.
Wie gerade erwähnt, gibt es nach dem Start von Nginx einen Master-Prozess und mehrere Worker-Prozesse. Der Master-Prozess wird hauptsächlich zum Verwalten von Arbeitsprozessen verwendet, einschließlich: Empfangen von Signalen von der Außenwelt, Senden von Signalen an jeden Arbeitsprozess, Überwachen des Betriebsstatus des Arbeitsprozesses und automatischer Neustart eines neuen Arbeitsprozesses, wenn der Arbeitsprozess beendet wird (siehe unten). ungewöhnliche Umstände).
Grundlegende Netzwerkereignisse werden im Worker-Prozess verarbeitet. Mehrere Worker-Prozesse sind Peer-to-Peer. Sie konkurrieren gleichermaßen um Anfragen von Kunden und jeder Prozess ist unabhängig voneinander. Eine Anfrage kann nur in einem Arbeitsprozess verarbeitet werden, und ein Arbeitsprozess kann keine Anfragen von anderen Prozessen verarbeiten. Die Anzahl der Arbeitsprozesse kann im Allgemeinen so eingestellt werden, dass sie mit der Anzahl der CPU-Kerne der Maschine übereinstimmt. Der Grund dafür ist untrennbar mit dem Prozessmodell und dem Ereignisverarbeitungsmodell von Nginx verbunden.
Was sollten wir nach dem Start von Nginx tun, wenn wir Nginx betreiben möchten?
Aus dem Obigen können wir ersehen, dass der Master den Worker-Prozess verwaltet, sodass wir nur mit dem Master-Prozess kommunizieren müssen. Der Masterprozess empfängt Signale von der Außenwelt und führt dann basierend auf den Signalen verschiedene Aktionen aus. Wenn wir also Nginx steuern möchten, müssen wir lediglich durch Kill ein Signal an den Master-Prozess senden. Beispielsweise weist kill -HUP pid Nginx an, Nginx ordnungsgemäß neu zu starten oder die Konfiguration neu zu laden. Da es ordnungsgemäß neu gestartet wird, wird der Dienst nicht unterbrochen. Was macht der Master-Prozess, nachdem er das HUP-Signal empfangen hat?
Nach Erhalt des Signals lädt der Masterprozess zunächst die Konfigurationsdatei neu, startet dann einen neuen Arbeitsprozess und sendet Signale an alle alten Arbeitsprozess, um ihnen mitzuteilen, dass sie ehrenhaft in den Ruhestand gehen können.
Nachdem der neue Worker gestartet ist, beginnt er, neue Anfragen zu empfangen, während der alte Worker nach Erhalt des Signals vom Master keine neuen Anfragen mehr empfängt und alle unverarbeiteten Anfragen im aktuellen Prozess verarbeitet , Ausfahrt.
Natürlich ist das Senden von Signalen direkt an den Masterprozess eine ältere Betriebsmethode. Nach der Nginx-Version 0.8 wurden eine Reihe von Befehlszeilenparametern eingeführt, um unsere Verwaltung zu erleichtern. Beispielsweise dient ./nginx -s reload dazu, Nginx neu zu starten, und ./nginx -s stop dient dazu, die Ausführung von Nginx zu stoppen.
Wie geht das?
Nehmen wir Reload als Beispiel. Wir sehen, dass wir beim Ausführen des Befehls einen neuen Nginx-Prozess starten und der neue Nginx-Prozess unseren Zweck kennt, nachdem er den Reload-Parameter analysiert hat Um die Konfigurationsdatei neu zu laden, wird ein Signal an den Master-Prozess gesendet. Die nächste Aktion ist dieselbe, als ob wir das Signal direkt an den Master-Prozess senden würden.
Jetzt wissen wir, was Nginx intern tut, wenn wir Nginx betreiben. Wie verarbeitet der Worker-Prozess die Anfrage? Wie bereits erwähnt, sind Arbeitsprozesse gleich und jeder Prozess hat die gleichen Möglichkeiten, Anfragen zu verarbeiten. Wenn wir einen HTTP-Dienst auf Port 80 bereitstellen und eine Verbindungsanfrage eingeht, kann jeder Prozess die Verbindung verarbeiten.
Zunächst wird jeder Worker-Prozess vom Master-Prozess abgezweigt. Im Master-Prozess wird zunächst der Socket (listenfd) eingerichtet, der abgehört werden muss, und dann werden mehrere Worker-Prozesse abgezweigt. Der Listenfd aller Worker-Prozesse wird lesbar, wenn eine neue Verbindung eintrifft. Um sicherzustellen, dass nur ein Prozess die Verbindung verarbeitet, greifen alle Worker-Prozesse vor der Registrierung des Listenfd-Leseereignisses auf den Aufruf des Listenfd-Lesevorgangs zurück Accept im Leseereignis, um die Verbindung zu akzeptieren.
Wenn ein Arbeitsprozess die Verbindung akzeptiert, beginnt er, die Anfrage zu lesen, die Anfrage zu analysieren, die Anfrage zu verarbeiten, Daten zu generieren und sie dann an den Client zurückzugeben und schließlich die Verbindung zu trennen ist Das ist es. Wir können sehen, dass eine Anfrage vollständig vom Worker-Prozess verarbeitet wird und nur in einem Worker-Prozess verarbeitet wird.
Multithreading-Modell vs. Multiprozessmodell, das ist eine Frage!
Was sind also die Vorteile der Übernahme dieses Prozessmodells durch Nginx? Natürlich wird es auf jeden Fall viele Vorteile geben. Erstens handelt es sich bei jedem Arbeitsprozess um einen unabhängigen Prozess, der nicht gesperrt werden muss, sodass der durch das Sperren verursachte Overhead eliminiert wird. Gleichzeitig ist die Programmierung und Problemsuche wesentlich komfortabler. Zweitens wirkt sich die Verwendung unabhängiger Prozesse nicht gegenseitig aus, nachdem ein Prozess beendet wurde, und der Dienst wird nicht unterbrochen. Der Masterprozess startet schnell einen neuen Arbeitsprozess. Wenn der Worker-Prozess abnormal beendet wird, muss natürlich ein Fehler im Programm vorliegen, der dazu führt, dass alle Anforderungen des aktuellen Workers fehlschlagen. Dies wirkt sich jedoch nicht auf alle Anforderungen aus, sodass das Risiko verringert wird. Natürlich gibt es viele Vorteile, und jeder kann sie langsam erleben.
Oben wurde viel über das Prozessmodell von Nginx gesprochen. Schauen wir uns als Nächstes an, wie Nginx mit Ereignissen umgeht.
Jemand fragt sich vielleicht, dass Nginx eine Multi-Worker-Methode verwendet, um Anfragen zu verarbeiten. Es gibt nur einen Hauptthread in jedem Worker, daher ist die Anzahl der gleichzeitigen Prozesse, die verarbeitet werden können, sehr begrenzt viele Parallelitäten? Wie erreicht man eine hohe Parallelität? Nein, das ist die Genialität von Nginx. Nginx verwendet eine asynchrone und nicht blockierende Methode, um Anfragen zu verarbeiten. Mit anderen Worten: Nginx kann Tausende von Anfragen gleichzeitig verarbeiten.
Denken Sie über die allgemeine Arbeitsmethode von Apache nach (Apache hat auch eine asynchrone, nicht blockierende Version, die jedoch nicht häufig verwendet wird, da sie mit einigen seiner eigenen Module in Konflikt steht. Jede Anforderung belegt a). Arbeitsthread Wenn die Anzahl der Parallelität Tausende erreicht, verarbeiten Tausende von Threads gleichzeitig Anforderungen. Dies stellt eine große Herausforderung für das Betriebssystem dar. Der von Threads belegte Speicher ist sehr groß, und die Leistung kann durch den Thread-Kontextwechsel nicht verbessert werden.
Synchronisiertes Blockieren vs. asynchrones Nichtblockieren
Warum kann Nginx asynchrones Nichtblockieren verwenden, um damit umzugehen, oder was genau ist asynchrones Nichtblockieren? Kehren wir zum Ausgangspunkt zurück und betrachten den gesamten Prozess einer Anfrage. Zuerst kommt die Anfrage, eine Verbindung wird aufgebaut und dann werden die Daten empfangen. Nach dem Empfang der Daten werden die Daten gesendet. Spezifisch für die unterste Schicht des Systems sind es die Lese- und Schreibereignisse. Wenn die Lese- und Schreibereignisse nicht bereit sind, müssen sie den Aufruf blockieren . Wenn die Veranstaltung noch nicht bereit ist, können sie nur warten. Sie können fortfahren, wenn die Veranstaltung bereit ist. Blockierende Aufrufe gelangen in den Kernel und warten, und die CPU wird von anderen verwendet. Dies ist offensichtlich nicht für Single-Threaded-Worker geeignet. Wenn es mehr Netzwerkereignisse gibt, wird niemand die CPU verwenden CPU-Auslastung im Leerlauf Natürlich kann die Rate nicht steigen, geschweige denn eine hohe Parallelität.
Okay, Sie sagten, das Hinzufügen der Anzahl von Prozessen, was ist der Unterschied zwischen diesem und dem Threading-Modell von Apache? Achten Sie darauf, keine unnötigen Kontextwechsel hinzuzufügen. Daher ist das Blockieren von Systemaufrufen in Nginx am tabu. Nicht blockieren, dann ist es nicht blockierend. Nicht blockierend bedeutet, dass, wenn die Veranstaltung noch nicht bereit ist, sofort zu EAGAIN zurückgekehrt wird, um Ihnen mitzuteilen, dass die Veranstaltung noch nicht bereit ist. Warum geraten Sie in Panik? Okay, überprüfen Sie das Ereignis nach einer Weile erneut, bis das Ereignis bereit ist. In diesem Zeitraum können Sie zunächst andere Dinge tun und dann prüfen, ob das Ereignis bereit ist. Obwohl es nicht mehr blockiert ist, müssen Sie von Zeit zu Zeit den Status des Ereignisses überprüfen. Sie können mehr tun, aber der Aufwand ist nicht gering. Daher gibt es einen asynchronen, nicht blockierenden Ereignisverarbeitungsmechanismus, und die spezifischen Systemaufrufe sind Systemaufrufe wie select/poll/epoll/kqueue.
Sie bieten einen Mechanismus, der es Ihnen ermöglicht, mehrere Ereignisse gleichzeitig zu überwachen. Sie können jedoch ein Timeout festlegen, wenn ein Ereignis innerhalb des Timeouts zurückkommt. Dieser Mechanismus löst lediglich unsere beiden oben genannten Probleme (in den folgenden Beispielen verwenden wir Epoll häufig als Beispiel, um diese Art von Funktion darzustellen). Das Ereignis ist bereit, wir gehen zum Lesen und Schreiben über. Wenn das Lesen und Schreiben EAGAIN zurückgibt, fügen wir es erneut zu Epoll hinzu. Auf diese Weise verarbeiten wir ein Ereignis, solange es bereit ist. Erst wenn nicht alle Ereignisse bereit sind, warten wir in Epoll. Auf diese Weise können wir eine große Anzahl gleichzeitiger Anfragen bearbeiten. Natürlich handelt es sich bei den gleichzeitigen Anfragen nur um einen Thread, daher kann natürlich nur eine Anfrage gleichzeitig bearbeitet werden Wechseln Sie einfach ständig zwischen Anforderungen. Der Wechsel wurde lediglich freiwillig vorgenommen, da das asynchrone Ereignis nicht bereit war. Der Wechsel verursacht hier keine Kosten. Sie können ihn als die Verarbeitung mehrerer vorbereiteter Ereignisse in einer Schleife verstehen, was tatsächlich der Fall ist.
Im Vergleich zum Multithreading bietet diese Ereignisverarbeitungsmethode große Vorteile: Es müssen keine Threads erstellt werden, jede Anforderung belegt nur sehr wenig Speicher, es gibt keinen Kontextwechsel und die Ereignisverarbeitung ist sehr einfach. Unabhängig davon, wie viele Parallelitäten vorhanden sind, führt dies nicht zu unnötiger Ressourcenverschwendung (Kontextwechsel). Mehr Parallelität beansprucht nur mehr Speicher. Ich habe die Anzahl der Verbindungen zuvor auf einem Computer mit 24G-Speicher getestet und die Anzahl der gleichzeitig verarbeiteten Anforderungen erreicht. Heutige Netzwerkserver verwenden grundsätzlich diese Methode, was auch der Hauptgrund für die hohe Leistung von Nginx ist.
Wir haben bereits gesagt, dass es empfehlenswert ist, die Anzahl der Worker auf die Anzahl der CPU-Kerne festzulegen. Es ist leicht zu verstehen, dass mehr Worker nur dazu führen, dass Prozesse um CPU-Ressourcen konkurrieren, was zu unnötigen Kontextwechseln führt.
Um die Multi-Core-Funktionen besser nutzen zu können, bietet Nginx außerdem eine CPU-Affinitätsbindungsoption. Wir können einen bestimmten Prozess an einen bestimmten Kern binden, sodass es keine Probleme aufgrund von Prozesswechseln gibt . Kommen Sie zu einem Cache-Fehler. Kleine Optimierungen wie diese sind in Nginx weit verbreitet und verdeutlichen auch die sorgfältigen Bemühungen des Nginx-Autors. Wenn Nginx beispielsweise 4-Byte-Strings vergleicht, konvertiert es die 4 Zeichen in einen int-Typ und vergleicht sie dann, um die Anzahl der CPU-Anweisungen usw. zu reduzieren.
Jetzt wissen wir, warum Nginx ein solches Prozessmodell und Ereignismodell wählt. Für einen einfachen Webserver gibt es normalerweise drei Arten von Ereignissen: Netzwerkereignisse, Signale und Timer. Aus der obigen Erklärung wissen wir, dass Netzwerkereignisse durch asynchrone Nichtblockierung gut gelöst werden können. Wie gehe ich mit Signalen und Timern um?
Erstens die Signalverarbeitung.
Für Nginx gibt es einige spezifische Signale, die bestimmte Bedeutungen darstellen. Das Signal unterbricht den aktuellen Programmablauf und setzt die Ausführung nach Zustandsänderung fort. Wenn es sich um einen Systemaufruf handelt, kann dies dazu führen, dass der Systemaufruf fehlschlägt und einen erneuten Einstieg erfordert. In Bezug auf die Signalverarbeitung können Sie einige Fachbücher lesen, daher werde ich hier nicht näher darauf eingehen. Wenn Nginx für Nginx auf ein Ereignis wartet (während epoll_wait) und das Programm ein Signal empfängt, gibt epoll_wait nach der Verarbeitung der Signalverarbeitungsfunktion einen Fehler zurück und das Programm kann dann erneut in den epoll_wait-Aufruf eintreten.
Werfen wir außerdem einen Blick auf den Timer. Da epoll_wait und andere Funktionen beim Aufruf ein Timeout festlegen können, verwendet Nginx dieses Timeout, um den Timer zu implementieren. Die Timer-Ereignisse in Nginx werden in einem rot-schwarzen Baum platziert, der die Timer verwaltet, bevor epoll_wait eingegeben wird. Die Mindestzeit aller Timer-Ereignisse wird aus dem rot-schwarzen Baum abgerufen und das Timeout von epoll_wait wird danach berechnet Zeit.
Wenn also kein Ereignis generiert wird und kein Interrupt-Signal vorliegt, tritt bei epoll_wait eine Zeitüberschreitung auf, d. h. das Timer-Ereignis ist eingetroffen. Zu diesem Zeitpunkt überprüft Nginx alle Timeout-Ereignisse, setzt ihren Status auf Timeout und verarbeitet dann das Netzwerkereignis. Daraus ist ersichtlich, dass wir beim Schreiben von Nginx-Code normalerweise als Erstes bei der Verarbeitung der Rückruffunktion eines Netzwerkereignisses das Timeout bestimmen und dann das Netzwerkereignis verarbeiten.
Weitere Informationen zu Nginx finden Sie in der Spalte Tutorial zur Nginx-Nutzung!
Das obige ist der detaillierte Inhalt vonWarum ist die Nginx-Leistung gut?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!