uWSGI-python-highload-Konfiguration
Wir haben eine große EC2-Instanz mit 32 Kernen, derzeit läuft Nginx, Tornado und Redis, portion im Durchschnitt 5K Anfragen pro Sekunde. Alles scheint gut zu funktionieren, aber die CPU-Last bereits das erreichen von 70% und wir haben die Unterstützung von noch mehr Anfragen. Einer der Gedanken war, Sie zu ersetzen Tornado mit uWSGI, weil wir nicht wirklich verwenden, async-features der Tornado.
Unsere Anwendung besteht aus einer Funktion, es erhält eine JSON - (~=4KB), etwas blockieren, aber sehr schnellen Sachen (Redis) und JSON zurückgeben.
- Proxy HTTP-Anfrage an einen der Tornado-Instanzen (Nginx)
- Parsen von HTTP-Requests (Tornado)
- POST Lesen Körper string (stringified JSON) und wandelt es in ein python-dictionary (Tornado)
- Daten aus Redis (Sperrung) auf derselben Maschine (py-redis mit hiredis)
- Verarbeiten der Daten (Python ist3.4)
- Update Redis auf der gleichen Maschine (py-redis mit hiredis)
- Vorbereiten stringified JSON-response (Python ist3.4)
- Senden der Antwort an den proxy (Tornado)
- Senden der Antwort an client (Nginx)
Dachten wir, dass die Verbesserung der Geschwindigkeit kommt von uwsgi-Protokoll, können wir Nginx installieren auf separaten server, und der proxy alle Anfragen an uWSGI mit uwsgi-Protokoll. Aber nach dem Versuch alle möglichen Konfigurationen und ändern von OS Parameter, die wir noch nicht bekommen kann es arbeiten auch auf die aktuelle Last.
Die meisten der Zeit, nginx log enthält 499 und 502 Fehler. Bei einigen Konfigurationen ist es gerade aufgehört zu empfangen neue Anfragen, wie es auf ein OS beschränken.
Also wie gesagt, wir haben 32 Kerne, 60GB Speicher frei und sehr schnellen Netzwerk. Das tun wir nicht, harte Sachen, nur sehr schnell blockierenden Operationen. Was ist die beste Strategie in diesem Fall? Prozesse, Threads, Async? Was OS-Parameter sollten gesetzt werden?
Aktuelle Konfiguration ist:
[uwsgi]
master = 2
processes = 100
socket = /tmp/uwsgi.sock
wsgi-file = app.py
daemonize = /dev/null
pidfile = /tmp/uwsgi.pid
listen = 64000
stats = /tmp/stats.socket
cpu-affinity = 1
max-fd = 20000
memory-report = 1
gevent = 1000
thunder-lock = 1
threads = 100
post-buffering = 1
Nginx config:
user www-data;
worker_processes 10;
pid /run/nginx.pid;
events {
worker_connections 1024;
multi_accept on;
use epoll;
}
OS config:
sysctl net.core.somaxconn
net.core.somaxconn = 64000
Ich weiß, die Grenzen sind zu hoch, begann zu versuchen, jeden Wert möglich.
UPDATE:
Landete ich mit der folgenden Konfiguration:
[uwsgi]
chdir = %d
master = 1
processes = %k
socket = /tmp/%c.sock
wsgi-file = app.py
lazy-apps = 1
touch-chain-reload = %dreload
virtualenv = %d.env
daemonize = /dev/null
pidfile = /tmp/%c.pid
listen = 40000
stats = /tmp/stats-%c.socket
cpu-affinity = 1
max-fd = 200000
memory-report = 1
post-buffering = 1
threads = 2
Du musst angemeldet sein, um einen Kommentar abzugeben.
Ich denke, deine Anfrage handling grob gliedert sich wie folgt:
Könnte man benchmark der Umgang mit Zeit auf einem fast idle system. Meine Vermutung ist, dass die round-trip-würde einkochen, 2 oder 3 Millisekunden. Bei 70% CPU-Last würde dies gehen bis zu etwa 4 oder 5 ms (nicht mitgerechnet die Zeit verbrachte in der nginx-Anfrage-Warteschlange, nur das handling in der uWSGI-Arbeiter).
Bei 5k req/s Ihre Durchschnittliche in-Prozess verlangen könnte, wäre in der 20 ... 25 Bereich. Ein anständiges match zu Ihrer VM.
Nächste Schritt ist, um die balance der CPU-Kerne. Wenn Sie 32 Kerne, ist es nicht sinnvoll, zu reservieren, 1000 worker-Prozesse. Sie könnten am Ende chocking das system auf die Kontext-switching-overhead. Ein guter Ausgleich wird die Summe der Arbeitnehmer (nginx+uWSGI+redis) in der Größenordnung wie die verfügbaren CPU-Kerne, vielleicht mit ein wenig extra Abdeckung für blocking I/O (z.B. Dateisystem, aber vor allem vernetzte Anfragen getan, um andere hosts, wie ein DBMS). Wenn die Blockierung I/O wird ein großer Teil der Gleichung betrachten Sie das umschreiben in der asynchronen Programmierung und Integration eines async-stack.
Erste Beobachtung: du bist Zuteilung von 10 Arbeitnehmern zu nginx. Jedoch ist die CPU-Zeit nginx verbringt eine Anfrage ist VIEL niedriger als die Zeit, uWSGI verbringt es. Ich würde anfangen, durch die Bereitstellung von über 10% der system-nginx (3 oder 4 worker-Prozesse).
Den Rest müsste aufgeteilt werden uWSGI und redis. Ich weiß nicht, über die Größe Ihrer indices in redis, oder über die Komplexität Ihrer python-code, aber mein Erster Versuch wäre eine 75%/25% aufgeteilt zwischen uWSGI und redis. Würde, dass redis auf über 6 Mitarbeitern und uWSGI auf über 20 Mitarbeiter + Meister.
Als für die-threads option in der uwsgi-Konfiguration: thread-Wechsel ist leichter als Prozess wechseln, aber wenn ein bedeutender Teil Ihrer python-code für die CPU-gebunden ist, wird es nicht Fliegen, weil der GIL. Threads-option ist vor allem interessant, wenn ein erheblicher Teil von Ihrem Umgang mit der Zeit ist der I/O blockiert. Sie könnten deaktivieren threads, oder versuchen, sich mit den Arbeitern=10 threads=2 " als einen ersten Versuch.
%Cpu(s): 7,8 us, 3,3 sy, 0,0 ni, 89,0 id, 0,0 wa, 0,0 hi, 0,0 si, 0,0 st
(user/system/idle/warten/hardware-interupt - /software-interupt/gestohlen). Siehe linuxaria.com/howto/understanding-the-top-command-on-linux für die Erklärung. drücken Sie1
im top zu sehen, Statistiken für jeden einzelnen CPU-Kern.