Nicht blockierend mit Python + uWSGI

Erreichen Sie eine asynchrone Verarbeitung mit nicht blockierender Kommunikation in Python

Es gibt Multiprozess-, Multithread- und nicht blockierende Methoden, um einen anderen Prozess auszuführen, bevor ein Prozess abgeschlossen ist. Ein Prozess ist eine Verarbeitungseinheit, die über einen eigenen Speicher verfügt, z. B. einen virtuellen Speicher, und der Speicher wird nicht von Prozessen gemeinsam genutzt. Da ein Thread eine Verarbeitungseinheit innerhalb eines Prozesses ist, teilen sich die Threads innerhalb desselben Prozesses den Speicher. Nicht blockierend kann auf mehrere Anfragen mit einem Thread antworten.

Blockierende Kommunikation und nicht blockierende Kommunikation

Es gibt zwei Kommunikationsmethoden für die Socket-Kommunikation: "Blockierende Kommunikation" und "Nicht blockierende Kommunikation". Das Blockieren ist eine Kommunikationsmethode, die auf den Abschluss des Sendens / Empfangens wartet und dann eine andere Verarbeitung startet. Daher wird sie häufig bei der sequentiellen Verarbeitung verwendet. Andererseits ist das Nichtblockieren eine Kommunikationsmethode, mit der eine andere Verarbeitung gestartet werden kann, auch wenn die Kommunikation nicht abgeschlossen ist. Daher wird sie häufig bei der Durchführung einer asynchronen Verarbeitung verwendet.

Nicht blockierend in Python 3.5

Ab Python 3.4 wurde der Standardbibliothek ein Modul namens "asyncio" hinzugefügt, um die nicht blockierende Verarbeitung zu ermöglichen (Referenz). Über asyncio ist es sehr leicht zu verstehen und in hier organisiert, und viele Beispiele werden veröffentlicht.

Nicht blockierender Modus mit uWSGI

Ab uWSGI 1.9 wird der nicht blockierende Modus unterstützt, wie in [hier] beschrieben (http://uwsgi-docs.readthedocs.io/en/latest/Async.html). Seit 2.0.4 wird auch "asyncio" unterstützt (Referenz).

Umgebung

Führen Sie unten in der Umgebung aus, in der Python 3.5 installiert ist.

  1. Installieren Sie greenlet
$ pip3 install greenlet
  1. Installieren Sie "uWSGI" mit "asyncio" -Unterstützung Suchen Sie zuerst das Verzeichnis, in dem greenlet installiert ist.
$ find / -name greenlet -type d

In der Umgebung von "Mac OS" war das Ergebnis "/ Users / xxx / .pyenv / version / 3.5.0 / include / python3.5m / greenlet". Führen Sie daher Folgendes aus.

$ CFLAGS="-I/Users/xxx/.pyenv/versions/3.5.0/include/python3.5m" UWSGI_PROFILE="asyncio" pip3 install uwsgi

Betriebsüberprüfung

Führen Sie eine einfache Überprüfung des Betriebs mit Blockierung und Nichtblockierung durch. Im Beispielcode wird die Datei gelesen, für diese Datei wird jedoch Folgendes verwendet.

numbers.txt


zero
one
two
three
four
five

Bei der Überprüfung wurde auch Apache Bench verwendet, aber bei Verwendung von local [hier](https://stackoverflow.com/questions/7938869/ab-is-erroring-out-with-apr-socket- Es ist notwendig, vorsichtig zu sein, wie in recv-connection-verweigert-61) beschrieben, und diesmal wird es mit dem folgenden Befehl ausgeführt.

$ ab -n 10000 -c 100 http://127.0.0.1:9090/

Betriebsüberprüfung mit Blockierung

$ pip3 unistall uwsgi

Stichprobe

Protokollieren Sie den Inhalt der gelesenen Datei.

def application(environ, start_response):
    def my_generator(name):
        with open(name) as lines:
            yield from lines

    g = my_generator("numbers.txt")
    for k, v in enumerate(g):
        print("%s:%s" % (k, v), end="")

    start_response('200 OK', [('Content-Type','text/html')])
    return [b"Hello World"]

Überprüfung

Führen Sie die Ausführung mit der Anzahl der Prozesse und Threads aus, die als 1 angegeben sind.

$ uwsgi --http-socket :9090 --processes 1 --threads 1 --logto uwsgi.log --wsgi-file webapp.py

Ergebnis

Ergebnisse von "Apache Bench" (nur einige).

Requests per second:    2694.06 [#/sec](mean)
Time per request:       37.119 [ms](mean)
Time per request:       0.371 [ms](mean, across all concurrent requests)
Transfer rate:          144.70 [Kbytes/sec] received

Betrieb ohne Makler

Protokollausgabe des Inhalts der Datei, die nach 5 Sekunden asynchron gelesen wird.

Stichprobe

import asyncio


@asyncio.coroutine
def my_generator(name):
    with open(name) as lines:
        yield from lines

def read():
    g = my_generator("numbers.txt")
    for k, v in enumerate(g):
        print("%s:%s" % (k, v), end="")


def application(environ, start_response):
    asyncio.get_event_loop().call_later(5, read)
    start_response('200 OK', [('Content-Type','text/html')])
    return [b"Hello World"]

Führen Sie dies aus, indem Sie 1 für die Anzahl der Prozesse und Threads angeben, wie im Fall des Blockierens.

$ uwsgi --asyncio 2 --http-socket :9090 --greenlet --processes 1 --threads 1 --logto uwsgi.log --wsgi-file webapp.py

Hier wird "2" für "--asyncio" angegeben, aber wenn es auf "1" gesetzt ist, wird Folgendes angezeigt und es startet nicht.

the greenlet suspend engine requires async mode

Wenn Sie in den "asynchronen Modus" schauen, wie unten beschrieben, gibt es eine Speicherstruktur, die als Kern bezeichnet wird und Daten für jede Anforderung speichert (definiert als Kern, da das Konzept der Threads verwirrend ist).

Technically, cores are simple memory structures holding request’s data, but to give the user 
the illusion of a multithreaded system we use that term.

Diese Kerne müssen ausgetauscht werden, und es sind wahrscheinlich nur zwei oder mehr.

Each core can manage a single request, so the more core you spawn, more requests you will be
able to manage (and more memory you will use). The job of the suspend/resume engines is to stop
the current request management, move to another core, and eventually come back to the old one
(and so on).

Ergebnis

Im Vergleich zum Blockieren ist dieses Beispiel in Bezug auf die Leistung etwas langsamer. Es scheint besser, mit anderen Proben zu vergleichen.

equests per second:    2336.16 [#/sec](mean)
Time per request:       42.805 [ms](mean)
Time per request:       0.428 [ms](mean, across all concurrent requests)
Transfer rate:          125.48 [Kbytes/sec] received

Da ich 2 Kerne im nicht blockierenden Modus verwende, habe ich "uWSGI" neu installiert und die Anzahl der Threads mit 2 überprüft. Das Ergebnis ist unten. Möglicherweise liegt ein Problem mit der Mac-Umgebung vor.

Requests per second:    2691.94 [#/sec](mean)
Time per request:       37.148 [ms](mean)
Time per request:       0.371 [ms](mean, across all concurrent requests)
Transfer rate:          144.59 [Kbytes/sec] received

Bemerkungen

Ich möchte "Asyncio" etwas genauer studieren, ein Framework wie "Flask" einführen und die Überprüfung fortsetzen, die unter bestimmten Bedingungen wie der Verwendung von Nginx auch als Forschung dient. Probieren Sie auch andere Sprachen aus, z. B. den Vergleich mit Golang.

Hier sagt "Wenn Sie Zweifel haben, verwenden Sie nicht den Async-Modus.", Aber es ist immer noch auf einem praktischen Niveau. Ist es einfach nicht erreicht? Oder haben Sie den richtigen Weg dazu?

Recommended Posts

Nicht blockierend mit Python + uWSGI
WebSocket mit Python + uWSGI
FizzBuzz in Python3
Scraping mit Python
Scraping mit Python
Python mit Go
Twilio mit Python
In Python integrieren
Spielen Sie mit 2016-Python
AES256 mit Python
Getestet mit Python
Python beginnt mit ()
mit Syntax (Python)
Bingo mit Python
Zundokokiyoshi mit Python
Excel mit Python
Mikrocomputer mit Python
Mit Python besetzen
Erstellen Sie mit Docker eine Python + uWSGI + Nginx-Umgebung
Serielle Kommunikation mit Python
Zip, entpacken mit Python
Django 1.11 wurde mit Python3.6 gestartet
Python mit Eclipse + PyDev.
Socket-Kommunikation mit Python
Datenanalyse mit Python 2
Scraping in Python (Vorbereitung)
Versuchen Sie es mit Python.
Python lernen mit ChemTHEATER 03
Sequentielle Suche mit Python
"Objektorientiert" mit Python gelernt
Umgang mit Yaml mit Python
Löse AtCoder 167 mit Python
Serielle Kommunikation mit Python
[Python] Verwenden Sie JSON mit Python
Python lernen mit ChemTHEATER 05-1
Lerne Python mit ChemTHEATER
Führen Sie prepDE.py mit python3 aus
1.1 Erste Schritte mit Python
Tweets mit Python sammeln
Binarisierung mit OpenCV / Python
3. 3. KI-Programmierung mit Python
Kernel-Methode mit Python
Scraping mit Python + PhantomJS
Tweets mit Python posten
Fahren Sie WebDriver mit Python
Verwenden Sie Mecab mit Python 3
[Python] Mit CGIHTTPServer umleiten
Sprachanalyse mit Python
Denken Sie an Yaml mit Python
Kinesis mit Python betreiben
Erste Schritte mit Python
Verwenden Sie DynamoDB mit Python
Zundko Getter mit Python
Hallo Welt mit Nginx + Uwsgi + Python auf EC2
Behandle Excel mit Python
Ohmsches Gesetz mit Python
Primzahlbeurteilung mit Python
Führen Sie Blender mit Python aus
Löse Mathe mit Python
Python ab Windows 7
Erstellen Sie mit Docker eine Umgebung aus Nginx + uWSGI + Python (Django)