Detaillierte Erläuterung der Implementierung der Nginx-Prozesssperre

Detaillierte Erläuterung der Implementierung der Nginx-Prozesssperre

1. Die Rolle der Nginx-Prozesssperre

Nginx ist eine Anwendung mit mehreren parallelen Prozessen. Um es ganz klar auszudrücken: Es gibt mehrere Arbeiter, die auf Netzwerkanforderungen hören. Wer eine Anforderung erhält, führt die nachfolgenden Transaktionen aus. Wenn keine Sperre vorhanden ist, ist dies das Szenario. Wenn das System eine Anforderung empfängt, verarbeitet der Prozess, der den Port überwachen kann, gleichzeitig die Transaktion. Natürlich wird das System verhindern, dass so schlimme Dinge passieren, aber es wird zu der sogenannten Herdenpanik kommen. (Ich weiß nicht, ob das richtig ist, aber wahrscheinlich habe ich das so gemeint)

Um zu vermeiden, dass viele Prozesse gleichzeitig abhören, sollten daher mehrere Worker der Reihe nach auf den Socket abhören. Um mehrere Worker geordnet anzuordnen, wird die in diesem Artikel beschriebene Prozesssperre angezeigt. Nur der Prozess, der die Sperre erhält, kann auf die Netzwerkanforderung zugreifen.

Das ist folgender Ablauf:

// Worker-Kerntransaktionsframework // ngx_event.c
Leere
ngx_process_events_and_timers(ngx_cycle_t *Zyklus)
{
    ngx_uint_t-Flags;
    ngx_msec_t-Zeitgeber, Delta;

    wenn (ngx_timer_resolution) {
        Timer = NGX_TIMER_INFINITE;
        Flaggen = 0;

    } anders {
        ngx_event_find_timer();
        Flags = NGX_UPDATE_TIME;

#wenn (NGX_WIN32)

        /* Signale vom Master im Falle einer Netzwerkinaktivität verarbeiten */

        wenn (Timer == NGX_TIMER_INFINITE || Timer > 500) {
            Zeitgeber = 500;
        }

#endif
    }

    wenn (ngx_use_accept_mutex) {
        // Um ​​Fairness zu gewährleisten, vermeiden Sie wiederholte Sperrkonflikte, wenn (ngx_accept_disabled > 0) {
            ngx_accept_disabled--;

        } anders {
            // Nur der Prozess, der die Sperre ergreift, führt die accept()-Operation auf dem Socket aus // Andere Worker verarbeiten zuvor verbundene Anfragen und Lese-/Schreiboperationen if (ngx_trylock_accept_mutex(cycle) == NGX_ERROR) {
                zurückkehren;
            }

            wenn (ngx_accept_mutex_held) {
                Flags |= NGX_POST_EVENTS;

            } anders {
                wenn (Timer == NGX_TIMER_INFINITE
                    || Timer > ngx_accept_mutex_delay)
                {
                    Zeitgeber = ngx_accept_mutex_delay;
                }
            }
        }
    }
    //Andere Kerntransaktionsverarbeitungif (!ngx_queue_empty(&ngx_posted_next_events)) {
        ngx_event_move_posted_next(Zyklus);
        Zeitgeber = 0;
    }

    delta = ngx_aktuelle_msec;

    (leer) ngx_process_events (Zyklus, Timer, Flags);

    delta = ngx_current_msec – delta;

    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, Zyklus->Protokoll, 0,
                   „Timer-Delta: %M“, delta);

    ngx_event_process_posted(Zyklus, &ngx_posted_accept_events);

    wenn (ngx_accept_mutex_held) {
        ngx_shmtx_unlock(&ngx_accept_mutex);
    }

    wenn (Delta) {
        ngx_event_expire_timers();
    }

    ngx_event_process_posted(Zyklus, &ngx_posted_events);
}
// Holen Sie sich die Sperre und registrieren Sie den Socket-Accept()-Prozess wie folgt ngx_int_t
ngx_trylock_accept_mutex(ngx_cycle_t *Zyklus)
{
    wenn (ngx_shmtx_trylock(&ngx_accept_mutex)) {

        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, Zyklus->Protokoll, 0,
                       "akzeptiere Mutex gesperrt");

        wenn (ngx_accept_mutex_held && ngx_accept_events == 0) {
            gib NGX_OK zurück;
        }

        wenn (ngx_enable_accept_events(Zyklus) == NGX_ERROR) {
            // Entsperrvorgang ngx_shmtx_unlock(&ngx_accept_mutex);
            gib NGX_ERROR zurück;
        }

        ngx_accept_events = 0;
        ngx_accept_mutex_held = 1;

        gib NGX_OK zurück;
    }

    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, Zyklus->Protokoll, 0,
                   „Akzeptieren der Mutex-Sperre fehlgeschlagen: %ui“, ngx_accept_mutex_held);

    wenn (ngx_accept_mutex_held) {
        wenn (ngx_disable_accept_events(cycle, 0) == NGX_ERROR) {
            gib NGX_ERROR zurück;
        }

        ngx_accept_mutex_held = 0;
    }

    gib NGX_OK zurück;
}

Mehr muss man zum Rest nicht sagen. Nur der Kern, also der Worker, der das Schloss schnappt, kann den Akzeptanzvorgang durchführen. Der Worker, dem es nicht gelingt, die Sperre zu ergattern, muss das vorherige Accept()-Recht aktiv freigeben. Auf diese Weise verarbeitet immer nur ein Worker gleichzeitig das Akzeptanzereignis.

2. Verwendung von Einstiegsschlössern

Dinge wie Schlösser haben normalerweise Schnittstellen, die von der Programmiersprache selbst definiert werden, oder eine feste Verwendung.

Beispielsweise synchronisiertes XXX in Java, Lock-bezogene gleichzeitige Paketsperren wie CountDownLatch, CyclicBarrier, ReentrantLock, ReentrantReadWriteLock, Semaphore …

Beispielsweise threading.Lock(), threading.RLock() in Python ...

Beispielsweise flock() in PHP ...

Der Grund für die Bezeichnung „Einstiegsebene“ liegt darin, dass es sich hier um Schnittstellen-APIs handelt. Sie müssen diese lediglich ohne weitere Kenntnisse entsprechend den Nutzungsspezifikationen anpassen. Doch tatsächlich ist es nicht einfach, die Einzelheiten sinnvoll zu nutzen.

3. Implementierung der Nginx-Prozesssperre

Da Nginx in der Sprache C geschrieben ist, liegt es definitiv näher an der untersten Ebene. Wenn wir sehen können, wie Sperren durch ihre Implementierung umgesetzt werden, sollten wir in der Lage sein, die tiefere Bedeutung von Sperren besser zu verstehen.

Im Allgemeinen umfassen Sperren die folgenden Hauptaspekte: Definition der Sperrdatenstruktur, Sperrlogik, Entsperrlogik sowie einige Benachrichtigungsmechanismen, Timeout-Mechanismen usw. Schauen wir uns einige dieser Anweisungen an und sehen wir, wie nginx sie implementiert:

3.1 Datenstruktur sperren

Zuerst müssen wir definieren, welche Variablen gesperrt sind, und dann einen Wert instanziieren, der mit mehreren Prozessen geteilt werden soll.

// Ereignis/ngx_event.c
//Globale Definition der Akzeptieren-Sperrvariablen ngx_shmtx_t ngx_accept_mutex;
// Dieses Schloss hat eine // atomare Implementierung mit dem volatilen Modifikator typedef volatile ngx_atomic_uint_t ngx_atomic_t;
typedef-Struktur {
#wenn (NGX_HAVE_ATOMIC_OPS)
    // Zum Implementieren von Sperren werden atomare Aktualisierungsvariablen verwendet, hinter denen sich der gemeinsam genutzte Speicherbereich befindet ngx_atomic_t *lock;
#wenn (NGX_HAVE_POSIX_SEM)
    ngx_atomic_t *warten;
    ngx_uint_t-Semaphor;
    sem_t sem;
#endif
#anders
    // FD wird verwendet, um die Sperre zu implementieren. Hinter FD steht eine Dateiinstanz ngx_fd_t fd;
    u_char *Name;
#endif
    ngx_uint_t drehen;
} ngx_shmtx_t;
// Definition der gemeinsam genutzten Speicherdatenstruktur typedef struct {
    u_char *Adresse;
    size_t Größe;
    ngx_str_t-Name;
    ngx_log_t *Protokoll;
    ngx_uint_t existiert; /* unsigned existiert:1; */
} ngx_shm_t;

3.2. FD-basierte Sperr-/Entsperrimplementierung

Sobald Sie eine Sperrinstanz haben, können Sie diese sperren und entsperren. Nginx verfügt über zwei Sperrimplementierungen, die hauptsächlich auf Plattformunterschieden beruhen: dateibasierte oder gemeinsam genutzte interne Implementierung. Basierend auf fd, also einer dateibasierten Implementierung, ist dies immer noch eine ziemlich aufwändige Operation. wie folgt:

// ngx_shmtx.c
ngx_uint_t
ngx_shmtx_trylock(ngx_shmtx_t *mtx)
{
    ngx_err_t Fehler;

    Fehler = ngx_trylock_fd(mtx->fd);

    wenn (err == 0) {
        Rückgabe 1;
    }

    wenn (err == NGX_EAGAIN) {
        gebe 0 zurück;
    }

#if __osf__ /* Tru64 UNIX */

    wenn (err == NGX_EACCES) {
        gebe 0 zurück;
    }

#endif

    ngx_log_abort(err, ngx_trylock_fd_n " %s fehlgeschlagen", mtx->name);

    gebe 0 zurück;
}
// core/ngx_shmtx.c
// 1. Sperrvorgang ngx_err_t
ngx_trylock_fd(ngx_fd_t fd)
{
    Struktur Herde fl;

    ngx_memzero(&fl, sizeof(Struktur flock));
    fl.l_type = F_WRLCK;
    fl.l_where = SEEK_SET;

    wenn (fcntl(fd, F_SETLK, &fl) == -1) {
        gib ngx_errno zurück;
    }

    gebe 0 zurück;
}
// os/unix/ngx_file.c
ngx_err_t
ngx_lock_fd(ngx_fd_t fd)
{
    Struktur Herde fl;

    ngx_memzero(&fl, sizeof(Struktur flock));
    fl.l_type = F_WRLCK;
    fl.l_where = SEEK_SET;
    // Rufe die vom System bereitgestellte Sperrmethode auf, if (fcntl(fd, F_SETLKW, &fl) == -1) {
        gib ngx_errno zurück;
    }

    gebe 0 zurück;
}

// 2. Implementierung freischalten // core/ngx_shmtx.c
Leere
ngx_shmtx_unlock(ngx_shmtx_t *mtx)
{
    ngx_err_t Fehler;

    Fehler = ngx_unlock_fd(mtx->fd);

    wenn (err == 0) {
        zurückkehren;
    }

    ngx_log_abort(err, ngx_unlock_fd_n " %s fehlgeschlagen", mtx->name);
}
// os/unix/ngx_file.c
ngx_err_t
ngx_unlock_fd(ngx_fd_t fd)
{
    Struktur Herde fl;

    ngx_memzero(&fl, sizeof(Struktur flock));
    fl.l_type = F_UNLCK;
    fl.l_where = SEEK_SET;

    wenn (fcntl(fd, F_SETLK, &fl) == -1) {
        gib ngx_errno zurück;
    }

    gebe 0 zurück;
}

Der entscheidende Punkt ist der Aufruf der System-API fcntl(), sonst nichts. Aus der Sicht eines Außenstehenden wird der Zweck der Prozesssperre natürlich erreicht, da die Vorgänge mehrerer Prozesse an Dateien sichtbar sind. Es gibt einige semantische Unterschiede zwischen tryLock und lock. Beim Versuch erhalten Sie einige Flags, die angeben, ob der Versuch erfolgreich war, aber beim direkten Sperren erhalten Sie keine Flags. Erfordert im Allgemeinen das Blockieren von Anfragen

3.3. Initialisierung der Nginx-Sperrinstanz

Vielleicht ist die Initialisierung einer Sperrinstanz an manchen Stellen nur eine einfache Zuweisung einer Variablen. Bei Nginx ist es jedoch etwas anders. Zunächst müssen Sie sicherstellen, dass jeder Mitarbeiter dieselbe oder gleichwertige Instanzen sehen kann. Da es sich beim Worker um einen vom Master abgezweigten Prozess handelt, kann garantiert werden, dass jeder Worker denselben Wert erhalten kann, solange die Sperre im Master instanziiert ist. Ist es also einfach so?

// Die Initialisierung der gemeinsamen Sperre wird im NGX-Master durchgeführt, dann fork() zum Arbeitsprozess // event/ngx_event.c
statisches ngx_int_t
ngx_event_module_init(ngx_cycle_t *Zyklus)
{
    ungültig ***cf;
    u_char *geteilt;
    size_t Größe, cl;
    // Einen gemeinsam genutzten Speicher definieren ngx_shm_t shm;
    ngx_time_t *tp;
    ngx_core_conf_t *ccf;
    ngx_event_conf_t *ecf;

    cf = ngx_get_conf(Zyklus->conf_ctx, ngx_events_module);
    ecf = (*cf)[ngx_event_core_module.ctx_index];

    wenn (!ngx_test_config && ngx_process <= NGX_PROCESS_MASTER) {
        ngx_log_error(NGX_LOG_NOTICE, Zyklus->Protokoll, 0,
                      "unter Verwendung der Ereignismethode \"%s\"", ecf->name);
    }

    ccf = (ngx_core_conf_t *) ngx_get_conf(Zyklus->conf_ctx, ngx_core_module);

    ngx_timer_resolution = ccf->timer_auflösung;

#wenn !(NGX_WIN32)
    {
    ngx_int_t-Grenze;
    Struktur rlimit rlmt;

    wenn (getrlimit(RLIMIT_NOFILE, &rlmt) == -1) {
        ngx_log_error(NGX_LOG_ALERT, Zyklus->Protokoll, ngx_errno,
                      "getrlimit(RLIMIT_NOFILE) fehlgeschlagen, ignoriert");

    } anders {
        wenn (ecf->Verbindungen > (ngx_uint_t) rlmt.rlim_cur
            && (ccf->rlimit_nofile == NGX_CONF_UNSET
                || ecf->Verbindungen > (ngx_uint_t) ccf->rlimit_nofile))
        {
            Limit = (ccf->rlimit_nofile == NGX_CONF_UNSET)?
                         (ngx_int_t) rlmt.rlim_cur: ccf->rlimit_nofile;

            ngx_log_error(NGX_LOG_WARN, Zyklus->Protokoll, 0,
                          „%ui worker_connections überschreiten“
                          "Ressourcenlimit für geöffnete Dateien: %i",
                          ecf->Verbindungen, Limit);
        }
    }
    }
#endif /* !(NGX_WIN32) */


    wenn (ccf->master == 0) {
        gib NGX_OK zurück;
    }

    wenn (ngx_accept_mutex_ptr) {
        gib NGX_OK zurück;
    }


    /* cl sollte gleich oder größer als die Cache-Zeilengröße sein */

    cl = 128;

    Größe = cl /* ngx_accept_mutex */
           + cl /* ngx_connection_counter */
           + cl; /* ngx_temp_nummer */

#wenn (NGX_STAT_STUB)

    Größe += cl /* ngx_stat_accepted */
           + cl /* ngx_stat_handled */
           + cl /* ngx_stat_requests */
           + cl /* ngx_stat_active */
           + cl /* ngx_stat_reading */
           + cl /* ngx_stat_writing */
           + cl; /* ngx_stat_waiting */

#endif

    shm.size = Größe;
    ngx_str_set(&shm.name, "nginx_shared_zone");
    shm.log = Zyklus->Protokoll;
    // Gemeinsam genutzten Speicherplatz zuordnen, mmap zur Implementierung verwenden, if (ngx_shm_alloc(&shm) != NGX_OK) {
        gib NGX_ERROR zurück;
    }

    geteilt = shm.Adresse;

    ngx_accept_mutex_ptr = (ngx_atomic_t *) geteilt;
    ngx_accept_mutex.spin = (ngx_uint_t) -1;
    // Basierend auf gemeinsam genutzten Dateien oder Speicherzuweisungsprozesssperren kann eine Mehrprozesssteuerung erreicht werden, wenn (ngx_shmtx_create(&ngx_accept_mutex, (ngx_shmtx_sh_t *) gemeinsam genutzt wird,
                         Zyklus->Lock_File.Data)
        != NGX_OK)
    {
        gib NGX_ERROR zurück;
    }

    ngx_connection_counter = (ngx_atomic_t *) (geteilt + 1 * cl);

    (leer) ngx_atomic_cmp_set(ngx_connection_counter, 0, 1);

    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, Zyklus->Protokoll, 0,
                   "Zähler: %p, %uA",
                   ngx_connection_counter, *ngx_connection_counter);

    ngx_temp_number = (ngx_atomic_t *) (geteilt + 2 * cl);

    tp = ngx_timeofday();

    ngx_random_number = (tp->msec << 16) + ngx_pid;

#wenn (NGX_STAT_STUB)

    ngx_stat_accepted = (ngx_atomic_t *) (geteilt + 3 * cl);
    ngx_stat_handled = (ngx_atomic_t *) (geteilt + 4 * cl);
    ngx_stat_requests = (ngx_atomic_t *) (geteilt + 5 * cl);
    ngx_stat_active = (ngx_atomic_t *) (geteilt + 6 * cl);
    ngx_stat_reading = (ngx_atomic_t *) (geteilt + 7 * cl);
    ngx_stat_writing = (ngx_atomic_t *) (geteilt + 8 * cl);
    ngx_stat_waiting = (ngx_atomic_t *) (geteilt + 9 * cl);

#endif

    gib NGX_OK zurück;
}
// core/ngx_shmtx.c
// 1. Verwenden Sie fd basierend auf dem gemeinsam genutzten Speicherplatz des Dateiprozesses
ngx_int_t
ngx_shmtx_create(ngx_shmtx_t *mtx, ngx_shmtx_sh_t *Adresse, u_char *Name)
{
    // Wird vom Masterprozess erstellt, ist also eine prozesssichere Operation und jeder Worker kann sie direkt verwenden if (mtx->name) {
        // Wenn es erstellt wurde, wurde fd zugewiesen und kann nicht erstellt werden. Teile fd einfach direkt. // Hinter fd steht eine Dateiinstanz if (ngx_strcmp(name, mtx->name) == 0) {
            mtx->name = Name;
            gib NGX_OK zurück;
        }

        ngx_shmtx_destroy(mtx);
    }
    // Verwenden Sie die Dateierstellung, um die Freigabe zu sperren mtx->fd = ngx_open_file(name, NGX_FILE_RDWR, NGX_FILE_CREATE_OR_OPEN,
                            NGX_FILE_DEFAULT_ACCESS);

    wenn (mtx->fd == NGX_INVALID_FILE) {
        ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno,
                      ngx_open_file_n " \"%s\" fehlgeschlagen", Name);
        gib NGX_ERROR zurück;
    }
    // Sobald es erstellt wurde, kann es gelöscht werden. Nachfolgende Sperrvorgänge werden nur basierend auf dieser fd-Instanz ausgeführt, wenn (ngx_delete_file(name) == NGX_FILE_ERROR) {
        ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_errno,
                      ngx_delete_file_n " \"%s\" fehlgeschlagen", Name);
    }

    mtx->name = Name;

    gib NGX_OK zurück;
}

// 2. Erstellen einer gemeinsamen Sperre basierend auf dem gemeinsamen Speicher // ngx_shmtx.c
ngx_int_t
ngx_shmtx_create(ngx_shmtx_t *mtx, ngx_shmtx_sh_t *Adresse, u_char *Name)
{
    mtx->lock = &addr->lock;

    wenn (mtx->spin == (ngx_uint_t) -1) {
        gib NGX_OK zurück;
    }

    mtx->spin = 2048;

#wenn (NGX_HAVE_POSIX_SEM)

    mtx->wait = &Adresse->wait;

    wenn (sem_init(&mtx->sem, 1, 0) == -1) {
        ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_errno,
                      "sem_init() ist fehlgeschlagen");
    } anders {
        mtx->Semaphor = 1;
    }

#endif

    gib NGX_OK zurück;
}
// os/unix/ngx_shmem.c
ngx_int_t
ngx_shm_alloc(ngx_shm_t *shm)
{
    shm->Adresse = (u_char *) mmap(NULL, shm->Größe,
                                PROT_READ|PROT_WRITE,
                                MAP_ANON|MAP_SHARED, -1, 0);

    wenn (shm->addr == MAP_FAILED) {
        ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,
                      „mmap(MAP_ANON|MAP_SHARED, %uz) ist fehlgeschlagen“, shm->Größe);
        gib NGX_ERROR zurück;
    }

    gib NGX_OK zurück;
}

Das Wesentliche der fd-basierten Sperrimplementierung basiert auf der Implementierung des dahinter liegenden Dateisystems. Da das Dateisystem für den Prozess sichtbar ist, ist die Steuerung desselben fd die Steuerung der gemeinsamen Sperre.

3.4. Implementierung des Sperrens/Entsperrens basierend auf gemeinsam genutztem Speicher

Der sogenannte Shared Memory ist eigentlich ein öffentlicher Speicherbereich, der außerhalb des Prozessbereichs liegt (und vom Betriebssystem verwaltet wird). Dies ist die Erstellung von mmap(), das wir zuvor gesehen haben und bei dem es sich um einen Teil des gemeinsam genutzten Speichers handelt.

// ngx_shmtx.c
ngx_uint_t
ngx_shmtx_trylock(ngx_shmtx_t *mtx)
{
    // Den Wert des gemeinsam genutzten Speicherbereichs direkt ändern // Eine erfolgreiche CAS-Änderung bedeutet eine erfolgreiche Sperre.
    Rückgabe (*mtx->lock == 0 und ngx_atomic_cmp_set (mtx->lock, 0, ngx_pid));
}

// Entsperren Sie den Vorgang der SHM-Version, CAS-Parsing, mit der Benachrichtigung void
ngx_shmtx_unlock(ngx_shmtx_t *mtx)
{
    wenn (mtx->spin != (ngx_uint_t) -1) {
        ngx_log_debug0(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0, "shmtx entsperren");
    }

    wenn (ngx_atomic_cmp_set(mtx->lock, ngx_pid, 0)) {
        ngx_shmtx_wakeup(mtx);
    }
}
//Benachrichtigen Sie den wartenden Prozess static void
ngx_shmtx_wakeup(ngx_shmtx_t *mtx)
{
#wenn (NGX_HAVE_POSIX_SEM)
    ngx_atomic_uint_t warte;

    wenn (!mtx->semaphore) {
        zurückkehren;
    }

    für ( ;; ) {

        warten = *mtx->warten;

        wenn ((ngx_atomic_int_t) warte <= 0) {
            zurückkehren;
        }

        wenn (ngx_atomic_cmp_set(mtx->warten, warten, warten - 1)) {
            brechen;
        }
    }

    ngx_log_debug1(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0,
                   „shmtx wake %uA“, warte);

    wenn (sem_post(&mtx->sem) == -1) {
        ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_errno,
                      "sem_post() ist beim Wake shmtx fehlgeschlagen");
    }

#endif
}

Die Implementierung der Shared-Memory-Version der Sperre besteht grundsätzlich im Setzen von Speichervariablen durch cas. Es ist nur so, dass dieser orientierte Speicher der Speicher des gemeinsam genutzten Bereichs ist.

4. Was bedeutet Schloss?

Ich habe schon viele Schleusen gesehen, aber an dieser komme ich immer noch nicht vorbei.

Was genau ist ein Schloss? Tatsächlich handelt es sich bei dem Schloss um ein Identifikationsbit. Wenn jemand diese Identifikationsposition sieht, wird er den Vorgang aktiv stoppen oder fortsetzen usw., sodass es wie ein Schloss aussieht. Dieses Flag kann in einem Objekt oder einem globalen Wert oder über verschiedene Medien wie Dateien, Redis oder ZK gesetzt werden. Es macht keinen Unterschied. Denn die entscheidende Frage ist nicht, wo es gespeichert wird, sondern wie dieses Flag sicher gesetzt werden kann.

Zur Implementierung von Sperren ist im Allgemeinen eine starke zugrunde liegende Bedeutungsgarantie erforderlich, wie etwa CAS-Operationen auf der CPU-Ebene und serielle atomare Warteschlangenoperationen auf der Anwendungsebene. . .
Speichersperre, Dateisperre und erweiterte Sperre haben alle ihre eigenen Anwendungsszenarien. Die Wahl der richtigen Schlösser ist der Schlüssel zur Bewertung. In diesem Moment sollten Sie in der Lage sein, es zu beurteilen!

Oben finden Sie eine ausführliche Erläuterung der Implementierung der Nginx-Prozesssperre. Weitere Informationen zur Nginx-Prozesssperre finden Sie in den anderen verwandten Artikeln auf 123WORDPRESS.COM!

Das könnte Sie auch interessieren:
  • Detaillierte Erläuterung des Mechanismus und der Implementierung der Accept-Sperre in Nginx
  • Lösen Sie das Problem, dass Nginx nach der Konfiguration von proxy_pass 404 zurückgibt
  • Lösung für den Konfigurationsfehler des Nginx-SSL-Zertifikats
  • Ursachen und Lösungen für den Nginx 502 Bad Gateway-Fehler
  • Proxy_pass-Methode in mehreren if in Nginx-Standorten
  • Beispielcode für die Nginx-Konfiguration zum Herunterladen von Dateien
  • Implementierung mehrerer Nginx-Standorte, die alle Anfragen weiterleiten oder auf statische Ressourcendateien zugreifen
  • So zeigen Sie den Nginx-Konfigurationsdateipfad und den Ressourcendateipfad an

<<:  Details nach dem Setzen des src des Iframes auf about:blank

>>:  Navicat für MySQL-Tutorial

Artikel empfehlen

Grafisches Tutorial zur Installation und Konfiguration von MySQL 8.0.21

Notieren Sie die Installations- und Konfiguration...

DHTML-Objekte (gemeinsame Eigenschaften verschiedener HTML-Objekte)

!DOCTYPE Gibt die Document Type Definition (DTD) ...

So erstellen Sie einen MySQL-Cluster mit hoher Verfügbarkeit und Leistung

Inhaltsverzeichnis Was ist MySQL NDB Cluster? Vor...

Binäre Installation von MySQL 5.7.23 unter CentOS7

Die Installationsinformationen im Internet sind u...

Erfahrungsaustausch zur Optimierung von MySQL-Big-Data-Abfragen (empfohlen)

Ernsthafte MySQL-Optimierung! Wenn die MySQL-Date...

Wie wirkt sich der zusammengesetzte Index von MySQL aus?

Inhaltsverzeichnis Hintergrund Zusammengesetzte I...

Drei nützliche Codes, damit sich Besucher an Ihre Website erinnern

Drei nützliche Codes, die Besuchern dabei helfen,...

Detaillierte Erläuterung der kombinierten MySQL-Indexmethode

Für jedes DBMS sind die Indizes der wichtigste Op...

Einfaches Tutorial zur Verwendung von Navicat für MySQL

empfehlen: Detailliertes Tutorial zur Registrieru...