Non bloquant avec Python + uWSGI

Réalisez un traitement asynchrone avec une communication non bloquante en Python

Il existe des méthodes multi-processus, multi-thread et non bloquantes pour exécuter un autre processus avant qu'un processus ne soit terminé. Un processus est une unité de traitement qui possède sa propre mémoire, telle qu'une mémoire virtuelle, et la mémoire n'est pas partagée entre les processus. Puisqu'un thread est une unité de traitement dans un processus, les threads dans le même processus partagent la mémoire. Le non-blocage peut répondre à plusieurs demandes avec un seul thread.

Communication bloquante et communication non bloquante

Il existe deux méthodes de communication pour la communication par socket, "communication bloquante" et "communication non bloquante". Le blocage est une méthode de communication qui attend la fin de la transmission / réception, puis commence un autre traitement, il est donc souvent utilisé lors de l'exécution d'un traitement séquentiel. D'autre part, le non-blocage est une méthode de communication qui peut démarrer un autre traitement même si la communication n'est pas terminée, elle est donc souvent utilisée lors de l'exécution d'un traitement asynchrone.

Non bloquant dans Python 3.5

Depuis Python 3.4, un module appelé ʻasyncio a été ajouté à la bibliothèque standard pour activer le traitement non bloquant ([Reference](http://qiita.com/icoxfog417/items/07cbf5110ca82629aca0)). À propos de ʻasyncio, il est très facile à comprendre et est organisé en ici, et de nombreux exemples sont publiés.

Mode non bloquant avec uWSGI

Depuis ʻuWSGI1.9, le mode non bloquant est supporté comme décrit dans [ici](http://uwsgi-docs.readthedocs.io/en/latest/Async.html). De plus, depuis 2.0.4, ʻasyncio est pris en charge (Reference).

Environnement

Ci-dessous, exécutez dans l'environnement où Python 3.5 est installé.

  1. Installez greenlet
$ pip3 install greenlet
  1. Installez ʻuWSGI avec le support ʻasyncio Tout d'abord, trouvez le répertoire où greenlet est installé.
$ find / -name greenlet -type d

Dans l'environnement de Mac OS, le résultat était / Users / xxx / .pyenv / versions / 3.5.0 / include / python3.5m / greenlet, alors exécutez ce qui suit.

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

Vérification du fonctionnement

Effectuez une vérification de fonctionnement simple avec blocage et non blocage. Dans l'exemple de code, le fichier est lu, mais ce qui suit est utilisé pour ce fichier.

numbers.txt


zero
one
two
three
four
five

De plus, lors de la vérification, ʻApache Bench` a été utilisé, mais lors de l'utilisation de local, [ici](https://stackoverflow.com/questions/7938869/ab-is-erroring-out-with-apr-socket- Il faut faire attention comme décrit dans recv-connection-refusée-61), et cette fois il est exécuté avec la commande suivante.

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

Vérification du fonctionnement avec blocage

$ pip3 unistall uwsgi

échantillon

Enregistrez le contenu du fichier lu.

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"]

Vérification

Exécutez avec le nombre de processus et de threads spécifié comme 1.

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

résultat

Résultats ʻApache Bench` (partiels seulement).

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

Fonctionnement en non-courtage

Sortie du journal du contenu du fichier lu de manière asynchrone après 5 secondes.

échantillon

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"]

Vérification

Exécutez en spécifiant 1 pour le nombre de processus et de threads comme dans le cas d'un blocage.

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

Ici, «2» est spécifié pour «--asyncio», mais lorsqu'il est défini sur «1», ce qui suit s'affiche et il ne démarre pas.

the greenlet suspend engine requires async mode

En regardant en «mode asynchrone», comme décrit ci-dessous, il y a une structure de mémoire appelée un noyau qui stocke les données pour chaque requête (définie comme un noyau car le concept de threads est déroutant).

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.

Ces cœurs doivent être commutés, et ce n'est probablement que deux ou plus.

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).

résultat

Comparé au blocage, cet échantillon est légèrement plus lent en termes de performances. Il semble préférable de comparer avec d'autres échantillons.

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

Comme j'utilise 2 cœurs en mode non bloquant, j'ai réinstallé ʻuWSGI` et vérifié le nombre de threads avec 2. Le résultat est ci-dessous. Cela peut être un problème d'environnement Mac.

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

Remarques

Je voudrais étudier un peu plus ʻasyncio, introduire un cadre tel que Flask`, et continuer la vérification qui sert également de recherche dans certaines conditions telles que l'utilisation de Nginx. Essayez également d'autres langages tels que la comparaison avec Golang.

Ici déclare «En cas de doute, n'utilisez pas le mode asynchrone», mais c'est toujours à un niveau pratique. N'est-il tout simplement pas atteint? Ou y a-t-il un moyen de le faire?

Recommended Posts

Non bloquant avec Python + uWSGI
WebSocket avec Python + uWSGI
FizzBuzz en Python3
Grattage avec Python
Grattage avec Python
Python avec Go
Twilio avec Python
Intégrer avec Python
Jouez avec 2016-Python
AES256 avec python
Testé avec Python
python commence par ()
avec syntaxe (Python)
Bingo avec python
Zundokokiyoshi avec python
Excel avec Python
Micro-ordinateur avec Python
Cast avec python
Créer un environnement Python + uWSGI + Nginx avec Docker
Communication série avec Python
Zip, décompressez avec python
Django 1.11 a démarré avec Python3.6
Python avec eclipse + PyDev.
Communication de socket avec Python
Analyse de données avec python 2
Grattage en Python (préparation)
Essayez de gratter avec Python.
Apprendre Python avec ChemTHEATER 03
Recherche séquentielle avec Python
"Orienté objet" appris avec python
Manipuler yaml avec python
Résolvez AtCoder 167 avec python
Communication série avec python
[Python] Utiliser JSON avec Python
Apprendre Python avec ChemTHEATER 05-1
Apprenez Python avec ChemTHEATER
Exécutez prepDE.py avec python3
1.1 Premiers pas avec Python
Collecter des tweets avec Python
Binarisation avec OpenCV / Python
3. 3. Programmation IA avec Python
Méthode Kernel avec Python
Grattage avec Python + PhantomJS
Publier des tweets avec python
Conduisez WebDriver avec python
Utiliser mecab avec Python 3
[Python] Redirection avec CGIHTTPServer
Analyse vocale par python
Pensez à yaml avec python
Utiliser Kinesis avec Python
Premiers pas avec Python
Utiliser DynamoDB avec Python
Getter Zundko avec python
Hello World avec nginx + uwsgi + python sur EC2
Gérez Excel avec python
Loi d'Ohm avec Python
Jugement des nombres premiers avec python
Exécutez Blender avec python
Résoudre des maths avec Python
Python à partir de Windows 7
Créer un environnement de Nginx + uWSGI + Python (Django) avec docker