
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 :
- Gunicorn mit Meinheld- Arbeitern
- Anzahl der Arbeiter: Anzahl der CPUs * 2 + 1
- Binden Sie den Socket an eine Unix-Adresse anstelle einer IP. Dadurch wird die Geschwindigkeit geringfügig erhöht .
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 networknginx.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