Vorwort nginx verwendet ein Multiprozessmodell. Wenn eine Anfrage eingeht, sperrt das System den Prozess, um sicherzustellen, dass nur ein Prozess die Anfrage akzeptiert.
1. Implementierung der Akzeptanzsperre 1.1 Was ist eine Kontosperre? Wenn wir über die Akzeptanzsperre sprechen, müssen wir das Thundering-Herd-Problem erwähnen. Das sogenannte Herdenwurfproblem bezieht sich auf einen Multiprozessserver wie Nginx. Wenn dieser nach dem Forking gleichzeitig auf demselben Port lauscht und eine externe Verbindung eingeht, werden alle ruhenden Kindprozesse geweckt, und letztendlich kann nur ein Kindprozess das Akzeptanzereignis erfolgreich verarbeiten, und die anderen Prozesse gehen wieder in den Ruhezustand. Dies führt zu vielen unnötigen Zeitplänen und Kontextwechseln und dieser Overhead ist völlig unnötig. In den neueren Versionen des Linux-Kernels wurde das durch den Accept-Aufruf selbst verursachte Thundering-Herd-Problem gelöst. In Nginx wird Accept jedoch vom Epoll-Mechanismus gehandhabt, und das durch Accept von Epoll verursachte Thundering-Herd-Problem wurde nicht gelöst (epoll_wait selbst kann nicht unterscheiden, ob das Leseereignis von einem Listen-Socket stammt, sodass alle Prozesse, die auf dieses Ereignis hören, von diesem epoll_wait geweckt werden.), sodass das Accept-Thundering-Herd-Problem von Nginx immer noch eine angepasste Lösung erfordert. Die Accept-Sperre ist die Lösung von nginx. Im Wesentlichen handelt es sich dabei um eine prozessübergreifende Mutex-Sperre, die sicherstellt, dass nur ein Prozess das Accept-Ereignis abhören kann. Die Accept-Sperre ist in der Praxis eine prozessübergreifende Sperre. Sie ist eine globale Variable in Nginx und wird wie folgt deklariert: ngx_shmtx_t ngx_accept_mutex; Dies ist eine Sperre, die zugewiesen wird, wenn das Ereignismodul initialisiert und in einem gemeinsam genutzten Speicher zwischen Prozessen abgelegt wird, um sicherzustellen, dass alle Prozesse auf diese Instanz zugreifen können. Das Sperren und Entsperren erfolgt durch CAS unter Verwendung atomarer Linux-Variablen. Wenn die Sperre fehlschlägt, wird sie sofort zurückgegeben. Es handelt sich um eine nicht blockierende Sperre. Der Freischaltcode lautet: statisch ngx_inline ngx_uint_t ngx_shmtx_trylock(ngx_shmtx_t *mtx) { Rückgabe (*mtx->lock == 0 und ngx_atomic_cmp_set (mtx->lock, 0, ngx_pid)); } #define ngx_shmtx_lock(mtx) ngx_spinlock((mtx)->lock, ngx_pid, 1024) #define ngx_shmtx_unlock(mtx) (void) ngx_atomic_cmp_set((mtx)->lock, ngx_pid, 0) Es ist ersichtlich, dass nach dem fehlgeschlagenen Aufruf von ngx_shmtx_trylock eine sofortige Rückkehr ohne Blockierung erfolgt. 1.2 Wie stellt die Akzeptanzsperre sicher, dass nur ein Prozess neue Verbindungen verarbeiten kann? Das durch Epoll verursachte Problem der Accept-Sperre lässt sich auch ganz einfach lösen. Sie müssen lediglich sicherstellen, dass gleichzeitig nur ein Prozess das Accept-Epoll-Ereignis registriert.
Dabei bleibt natürlich die Verarbeitung verzögerter Ereignisse außer Acht, worauf wir später noch eingehen werden. Die Verarbeitung der Akzeptanzsperre sowie die Registrierung und Stornierung von Akzeptanzereignissen in Epoll werden alle in ngx_trylock_accept_mutex durchgeführt. Diese Reihe von Prozessen wird in void ngx_process_events_and_timers(ngx_cycle_t *cycle) ausgeführt, das in der Nginx-Hauptschleife wiederholt aufgerufen wird. Das heißt, jede Runde der Ereignisverarbeitung konkurriert zunächst um die Akzeptanzsperre. Wenn der Wettbewerb erfolgreich ist, wird das Akzeptanzereignis in epoll registriert. Wenn er fehlschlägt, wird die Registrierung des Akzeptanzereignisses aufgehoben. Nachdem das Ereignis verarbeitet wurde, wird die Akzeptanzsperre freigegeben. Auf diese Weise hört nur ein Prozess auf einen Listen-Socket, wodurch das Thundering-Herd-Problem vermieden wird. 1.3 Welche Anstrengungen unternimmt der Ereignisbehandlungsmechanismus, um zu verhindern, dass die Akzeptanzsperre für längere Zeit belegt ist? Die Lösung, das Thundering-Herd-Problem mithilfe einer Accept-Sperre zu lösen, scheint sehr schön zu sein, aber wenn die obige Logik vollständig angewendet wird, treten Probleme auf: Wenn der Server sehr ausgelastet ist und viele Ereignisse zu verarbeiten hat, dauert die „Verarbeitung aller Ereignisse“ sehr lange, d. h. ein Prozess belegt die Accept-Sperre für eine lange Zeit und hat keine Zeit, neue Verbindungen zu verarbeiten; andere Prozesse belegen die Accept-Sperre nicht und können auch keine neuen Verbindungen verarbeiten – an diesem Punkt befindet sich die neue Verbindung in einem Zustand, in dem sie von niemandem verarbeitet wird, was zweifellos fatal für die Echtzeitleistung des Dienstes ist. Um dieses Problem zu lösen, verschiebt Nginx die Ereignisverarbeitung. Das heißt, bei der Verarbeitung von ngx_process_events werden Ereignisse nur in zwei Warteschlangen gestellt: ngx_thread_volatile ngx_event_t *ngx_posted_accept_events; ngx_thread_volatile ngx_event_t *ngx_posted_events; Verarbeiten Sie nach der Rückkehr zuerst ngx_posted_accept_events, heben Sie die Akzeptanzsperre sofort auf und verarbeiten Sie dann langsam andere Ereignisse. Das heißt, ngx_process_events verarbeitet nur epoll_wait, und der Verbrauch von Ereignissen erfolgt nach der Freigabe der Accept-Sperre, um die Zeit, die für die Accept-Belegung benötigt wird, zu minimieren und anderen Prozessen genügend Zeit für die Verarbeitung von Accept-Ereignissen zu geben. Wie wird dies also konkret erreicht? Tatsächlich geht es darum, ein NGX_POST_EVENTS-Flag im Flags-Parameter von Dadurch wird lediglich die langfristige Belegung der Akzeptanzsperre durch Ereignisverbrauch vermieden. Was also, wenn epoll_wait selbst lange dauert? Es ist nicht unmöglich, dass dies passiert. Die Verarbeitung in dieser Hinsicht ist ebenfalls sehr einfach. epoll_wait selbst hat eine Timeout-Periode, also begrenzen Sie einfach seinen Wert. Dieser Parameter wird in der globalen Variable ngx_accept_mutex_delay gespeichert. Unten finden Sie den Implementierungscode von ngx_process_events_and_timers, der Ihnen eine ungefähre Vorstellung von der zugehörigen Verarbeitung geben kann: Leere ngx_process_events_and_timers(ngx_cycle_t *Zyklus) { ngx_uint_t-Flags; ngx_msec_t-Zeitgeber, Delta; /* Etwas Code für die Verarbeitung von Zeitereignissen weglassen*/ // Dies ist die Zeit, um die Lastausgleichssperre zu verarbeiten und die Sperre zu akzeptieren, wenn (ngx_use_accept_mutex) { // Wenn der Wert des Load Balancing Tokens größer als 0 ist, bedeutet dies, dass die Last voll ist und die Annahme nicht mehr verarbeitet wird. Gleichzeitig wird der Wert um eins reduziert, wenn (ngx_accept_disabled > 0) { ngx_accept_disabled--; } anders { // Versuchen Sie, die Akzeptanzsperre zu erhalten, wenn (ngx_trylock_accept_mutex(cycle) == NGX_ERROR) { zurückkehren; } // Nachdem du die Sperre erhalten hast, füge das Flag zum Post-Flag hinzu, um die Verarbeitung aller Ereignisse zu verschieben. // Um zu vermeiden, dass die Accept-Sperre zu lange belegt wird, if (ngx_accept_mutex_held) { Flags |= NGX_POST_EVENTS; } anders { wenn (Timer == NGX_TIMER_INFINITE || Timer > ngx_accept_mutex_delay) { Timer = ngx_accept_mutex_delay; // Warten Sie höchstens ngx_accept_mutex_delay Millisekunden, um zu verhindern, dass die Akzeptanzsperre zu lange belegt ist} } } } delta = ngx_aktuelle_msec; // Rufen Sie process_events des Ereignisverarbeitungsmoduls auf, um eine epoll_wait-Methode zu verarbeiten (void) ngx_process_events(cycle, timer, flags); delta = ngx_current_msec - delta; //Berechnen Sie die für die Verarbeitung von Ereignissen benötigte Zeit ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, „Timer-Delta: %M“, delta); // Wenn ein verzögertes Akzeptanzereignis vorliegt, dann verschiebe die Verarbeitung dieses Ereignisses, wenn (ngx_posted_accept_events) { ngx_event_process_posted(Zyklus, &ngx_posted_accept_events); } // Akzeptanzsperre aufheben, wenn (ngx_accept_mutex_held) { ngx_shmtx_unlock(&ngx_accept_mutex); } // Alle Timeout-Ereignisse verarbeiten if (delta) { ngx_event_expire_timers(); } ngx_log_debug1(NGX_LOG_DEBUG_EVENT, Zyklus->Protokoll, 0, "gepostete Ereignisse %p", ngx_posted_events); wenn (ngx_posted_events) { wenn (ngx_threaded) { ngx_wakeup_worker_thread(Zyklus); } anders { // Alle aufgeschobenen Ereignisse verarbeiten ngx_event_process_posted(cycle, &ngx_posted_events); } } } Werfen wir einen Blick auf die zugehörige Verarbeitung von ngx_epoll_process_events: // Ereignisse lesen if ((revents & EPOLLIN) && rev->active) { wenn ((flags & NGX_POST_THREAD_EVENTS) und !rev->accept) { rev->gepostet_bereit = 1; } anders { rev->bereit = 1; } wenn (Flags & NGX_POST_EVENTS) { Warteschlange = (ngx_event_t **) (rev->akzeptieren? &ngx_posted_accept_events: &ngx_posted_events); ngx_locked_post_event(rev, Warteschlange); } anders { rev->handler(rev); } } wev = c->schreiben; // Ereignis schreiben, wenn ((revents & EPOLLOUT) && wev->active) { wenn (Flags & NGX_POST_THREAD_EVENTS) { wev->gepostet_bereit = 1; } anders { wir->bereit = 1; } wenn (Flags & NGX_POST_EVENTS) { ngx_locked_post_event(wev, &ngx_posted_events); } anders { wev->handler(wev); } } Die Verarbeitung ist auch relativ einfach. Wenn die Akzeptanzsperre erhalten wird, wird ein NGX_POST_EVENTS-Flag angezeigt und in die entsprechende Warteschlange gestellt. Wenn nicht, wird das Ereignis direkt verarbeitet. Zusammenfassen Das Obige ist der vollständige Inhalt dieses Artikels. Ich hoffe, dass der Inhalt dieses Artikels einen gewissen Lernwert für Ihr Studium oder Ihre Arbeit hat. Wenn Sie Fragen haben, können Sie eine Nachricht hinterlassen. Vielen Dank für Ihre Unterstützung von 123WORDPRESS.COM. Das könnte Sie auch interessieren:
|
<<: So fragen Sie doppelte Daten in einer MySQL-Tabelle ab
>>: Die Verwendung von setState in React und die Verwendung von synchron und asynchron
1. Notieren Sie mehrere Methoden zum Zentrieren d...
Vorwort Für Datei- oder Verzeichnisberechtigungen...
Der W3C-Standardisierungsprozess ist in 7 verschi...
Beim Erlernen von CSS3 geht es mehr darum, sich m...
1. Fügen Sie den folgenden Code zu http{} in ngin...
1. Hintergrund 1. Stellen Sie kurz den Shared Sto...
In diesem Artikel wird der spezifische Code für J...
vue+element UI kapselt eine öffentliche Funktion ...
Inhaltsverzeichnis Natives JS So senden Sie eine ...
<Vorlage> <div Klasse="Demo"&g...
MySQL-Konsistenzprotokoll Was passiert mit nicht ...
Inhaltsverzeichnis Warum brauchen wir Partitionen...
Inhaltsverzeichnis Weiterleitungsproxy Nginx-Reve...
Nachdem das Formular übermittelt wurde, wird die z...
Dieser Artikel beschreibt die Verwendung gespeich...