Optimierung: Konfigurieren des Nginx-Webservers zur Verbesserung der RPS-Leistung in der HTTP-API



Bevor Sie Ihre Infrastruktur skalieren und skalieren, müssen Sie zunächst sicherstellen, dass die Ressourcen ordnungsgemäß verwendet werden und die Anwendungskonfiguration die Leistung nicht beeinträchtigt. Das Hauptziel des Engineering-Teams besteht darin, den kontinuierlichen, unterbrechungsfreien Betrieb jedes entworfenen und bereitgestellten Systems mit minimalen Ressourcen sicherzustellen.



Wir waren mit dem oben genannten Problem konfrontiert, bei dem unser bereitgestelltes System täglich von einer Million Benutzern verwendet wurde, die von Zeit zu Zeit in Bursts eine Verbindung herstellten. Dies bedeutet, dass die Bereitstellung oder Skalierung mehrerer Server in dieser Situation nicht die beste Lösung ist.



In diesem Artikel geht es darum, Nginx zu optimieren, um die Leistung zu verbessern, dh um den RPS (Requests Per Second) in der HTTP-API zu erhöhen. Ich habe versucht, Sie über die Optimierung zu informieren, die wir im bereitgestellten System angewendet haben, um Zehntausende von Anforderungen pro Sekunde zu verarbeiten, ohne eine große Menge an Ressourcen zu verschwenden.



Aktionsplan: Sie müssen die HTTP-API (in Python mit flask geschrieben) ausführen, die mit Nginx Proxy ist. Eine hohe Bandbreite ist erforderlich. Der API-Inhalt ändert sich in Intervallen von einem Tag.



Optimierungs-

Nomen-



Prozess zum Erreichen des besten Ergebnisses; die effizienteste Nutzung einer Situation oder Ressource.


Wir haben Supervisor verwendet , um WSGI Server mit den folgenden Konfigurationen zu starten :





Der Supervisor-Befehl sieht folgendermaßen aus:



gunicorn api:app --workers=5 --worker-
class=meinheld.gmeinheld.MeinheldWorker --bind=unix:api.sock


Wir haben versucht, die Nginx-Konfiguration zu optimieren und überprüft, was für uns am besten funktioniert.



Um die Leistung der API zu bewerten, haben wir wrk mit dem folgenden Befehl verwendet:



wrk -t20 -c200 -d20s http://api.endpoint/resource


Standardkonfiguration



Wir haben zuerst einen Lasttest der API ohne Änderungen durchgeführt und die folgenden Statistiken erhalten:



Running 20s test @ http://api.endpoint/resource
  20 threads and 200 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   192.48ms  274.78ms   1.97s    87.18%
    Req/Sec    85.57     29.20   202.00     72.83%
  33329 requests in 20.03s, 29.59MB read
  Socket errors: connect 0, read 0, write 0, timeout 85
Requests/sec:   1663.71
Transfer/sec:      1.48MB


Aktualisieren der Standardkonfiguration



Aktualisieren wir die Standard-Nginx-Konfiguration, d. H. Nginx.conf in /etc/nginx/nginx.conf



worker_processes auto;
#or should be equal to the CPU core, you can use `grep processor /proc/cpuinfo | wc -l` to find; auto does it implicitly.

worker_connections 1024;
# default is 768; find optimum value for your server by `ulimit -n`

access_log off;
# to boost I/O on HDD we can disable access logs
# this prevent nginx from logging every action in a log file named `access.log`.

keepalive_timeout 15;
# default is 65;
# server will close connection after this time (in seconds)

gzip_vary on;
gzip_proxied any;
gzip_comp_level 2;
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_min_length 256;
gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
# reduces the data that needs to be sent over the network
nginx.conf (/etc/nginx/nginx.conf)



Nach den Änderungen führen wir die Konfigurationsprüfung durch:



sudo nginx -t


Wenn die Prüfung erfolgreich ist, können Sie Nginx neu starten, um die Änderungen widerzuspiegeln:



sudo service nginx restart


Mit dieser Konfiguration haben wir Lasttests der API durchgeführt und das folgende Ergebnis erhalten:



Running 20s test @ http://api.endpoint/resource
  20 threads and 200 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   145.80ms  237.97ms   1.95s    89.51%
    Req/Sec   107.99     41.34   202.00     66.09%
  42898 requests in 20.03s, 39.03MB read
  Socket errors: connect 0, read 0, write 0, timeout 46
  Non-2xx or 3xx responses: 2
Requests/sec:   2141.48
Transfer/sec:      1.95MB


Diese Konfigurationen reduzierten Zeitüberschreitungen und erhöhten RPS (Anforderungen pro Sekunde), aber nicht viel.



Nginx-Cache hinzufügen



Da in unserem Fall der Inhalt des Endpunkts im Abstand von einem Tag aktualisiert wird, wird eine geeignete Umgebung zum Zwischenspeichern von API-Antworten erstellt.



Das Hinzufügen eines Caches macht ihn jedoch ungültig. Dies ist eine von zwei Schwierigkeiten hier.

In der Informatik gibt es nur zwei Komplikationen: Ungültigmachen des Caches und Benennen von Dingen. - Phil Carlton



Wir wählen eine einfache Lösung, um das Cache-Verzeichnis mit einem Cronjob zu löschen, nachdem der Inhalt auf dem Downstream-System aktualisiert wurde.



Als nächstes wird Nginx die ganze harte Arbeit erledigen, aber jetzt müssen wir sicher sein, dass Nginx zu 100% bereit ist!



Um Caching zu Nginx hinzuzufügen, müssen Sie der Nginx-Konfigurationsdatei mehrere Anweisungen hinzufügen.



Vorher müssen wir ein Verzeichnis erstellen, um die Cache-Daten zu speichern:



sudo mkdir -p /data/nginx/cache


Änderungen der Nginx-Konfiguration:



proxy_cache_path /data/nginx/cache keys_zone=my_zone:10m inactive=1d;
server {
    ...
    location /api-endpoint/ {
        proxy_cache my_zone;
        proxy_cache_key "$host$request_uri$http_authorization";
        proxy_cache_valid 404 302 1m;
        proxy_cache_valid 200 1d;
        add_header X-Cache-Status $upstream_cache_status;
    }
    ...
}


Proxied Requests zwischenspeichern (Nginx-Konfiguration)



Nach dieser Konfigurationsänderung haben wir die API geladen und das folgende Ergebnis erhalten:



Running 20s test @ http://api.endpoint/resource
  20 threads and 200 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     6.88ms    5.44ms  88.91ms   81.36%
    Req/Sec     1.59k   500.04     2.95k    62.50%
  634405 requests in 20.06s, 589.86MB read
Requests/sec:  31624.93
Transfer/sec:     29.40MB


Durch das Hinzufügen von Caching konnten wir die Leistung um fast das 19-fache steigern.

Hinweis eines Timeweb-Experten :



Es ist wichtig zu beachten , dass das Zwischenspeichern von Abfragen, die in die Datenbank schreiben, zu einer zwischengespeicherten Antwort führt, jedoch nicht zum Schreiben in die Datenbank.

Nginx-Cache im RAM (Direktzugriffsspeicher)



Gehen wir noch einen Schritt weiter! Derzeit werden unsere Cache-Daten auf der Festplatte gespeichert. Was ist, wenn wir diese Daten im RAM speichern? In unserem Fall sind die Antwortdaten begrenzt und nicht groß.



Zunächst müssen Sie ein Verzeichnis erstellen, in dem der RAM-Cache bereitgestellt wird:



sudo mkdir -p /data/nginx/ramcache


Verwenden Sie den folgenden Befehl, um das erstellte Verzeichnis mit tmpfs im RAM bereitzustellen :



sudo mount -t tmpfs -o size=256M tmpfs /data/nginx/ramcache


Dadurch werden / data / nginx / ramcache im RAM bereitgestellt und 256 MB zugewiesen .



Wenn Sie glauben, den RAM-Cache deaktivieren zu wollen, führen Sie einfach den folgenden Befehl aus:



sudo umount /data/nginx/ramcache


Um das Cache-Verzeichnis im RAM nach dem Neustart automatisch neu zu erstellen, müssen wir die Datei / etc / fstab aktualisieren . Fügen Sie die folgende Zeile hinzu:



tmpfs /data/nginx/ramcache tmpfs defaults,size=256M 0 0


Hinweis: Wir müssen auch den Wert proxy_cache_path mit dem Pfad zu ramcache ( / data / nginx / ramcache ) registrieren .



Nach dem Aktualisieren der Konfiguration haben wir erneut einen API-Auslastungstest durchgeführt und das folgende Ergebnis erhalten:



Running 20s test @ http://api.endpoint/resource
  20 threads and 200 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     5.57ms    5.69ms 277.76ms   92.94%
    Req/Sec     1.98k   403.94     4.55k    71.77%
  789306 requests in 20.04s, 733.89MB read
Requests/sec:  39387.13
Transfer/sec:     36.62MB


Das Speichern des Caches im RAM führte zu einer fast 23-fachen signifikanten Verbesserung .



Gepuffertes Zugriffsprotokoll



Wir führen ein Protokoll über den Zugriff auf Proxy-Anwendungen. Sie können das Protokoll jedoch zuerst in einem Puffer speichern und erst dann auf die Festplatte schreiben:



  • wenn die nächste Zeile des Protokolls nicht in den Puffer passt
  • Wenn die Daten im Puffer älter sind als im Parameter flush angegeben .


Durch dieses Verfahren wird die bei jeder Anforderung durchgeführte Aufzeichnungsfrequenz verringert. Dazu müssen wir nur die Puffer- und Flush- Parameter mit dem entsprechenden Wert in der Direktive access_log hinzufügen :



location / {
    ...
    access_log /var/log/nginx/fast_api.log combined buffer=256k flush=10s;
    error_log /var/log/nginx/fast_api.err.log;
}


Pufferprotokoll vor dem Schreiben auf die Festplatte



Entsprechend der obigen Konfiguration werden die Zugriffsprotokolle zunächst nur dann gepuffert und auf der Festplatte gespeichert, wenn der Puffer 256 KB erreicht oder die gepufferten Daten älter als 10 Sekunden sind.



Hinweis: Der Name lautet hier log_format kombiniert .



Nach wiederholten Stresstests haben wir folgendes Ergebnis erhalten:



Running 20s test @ http://api.endpoint/resource
  20 threads and 200 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     4.21ms    3.19ms  84.83ms   83.84%
    Req/Sec     2.53k   379.87     6.02k    77.05%
  1009771 requests in 20.03s, 849.31MB read
Requests/sec:  50413.44
Transfer/sec:     42.40MB


Diese Konfiguration erhöhte die Anzahl der Anforderungen pro Sekunde erheblich, etwa 30-mal im Vergleich zur Anfangsphase.



Ausgabe



In diesem Artikel haben wir den Prozess der Optimierung der Nginx-Konfiguration zur Verbesserung der RPS-Leistung erläutert. Der RPS wurde von 1663 auf ~ 50413 erhöht ( eine etwa 30-fache Erhöhung ), was einen hohen Durchsatz ermöglicht. Durch Anpassen der Standardeinstellungen können Sie die Systemleistung verbessern.



Beenden wir den Artikel mit einem Zitat:

Lass es zuerst funktionieren. Dann mach es richtig. Dann optimieren. - Kent Beck

Quellen






All Articles