wake-up-neo.com

Machen Sie eine nicht blockierende Anforderung mit Anforderungen, wenn Sie Flask mit Gunicorn und Gevent ausführen

Meine Flaschenanwendung empfängt eine Anforderung, führt einige Verarbeitungsschritte aus und sendet dann eine Anforderung an einen langsamen externen Endpunkt, für den eine Antwort von 5 Sekunden benötigt wird. Es sieht so aus, als ob Gunicorn mit Gevent laufen kann, um viele dieser langsamen Anfragen gleichzeitig bearbeiten zu können. Wie kann ich das Beispiel unten ändern, damit die Ansicht nicht blockiert?

import requests

@app.route('/do', methods = ['POST'])
def do():
    result = requests.get('slow api')
    return result.content
gunicorn server:app -k gevent -w 4
17
JLTChiu

Wenn Sie Ihre Flask-Anwendung mit gunicorn bereitstellen, ist dies bereits nicht blockierend. Wenn ein Client auf eine Antwort von einer Ihrer Ansichten wartet, kann ein anderer Client ohne Probleme eine Anfrage an dieselbe Ansicht richten. Es wird mehrere Mitarbeiter geben, um mehrere Anforderungen gleichzeitig zu bearbeiten. Sie müssen Ihren Code nicht ändern, damit dies funktioniert. Dies gilt auch für fast alle Flask-Implementierungsoptionen.

10
sytech

Zunächst ein wenig Hintergrund: Ein blockierender Socket ist die Standardart des Sockets, sobald Sie mit dem Lesen Ihrer App oder des Threads beginnen, die Kontrolle erst wieder zu gewinnen, wenn die Daten tatsächlich gelesen werden oder die Verbindung getrennt wird. So arbeitet python-requests standardmäßig. Es gibt ein Spin-Off namens grequests, das nicht blockierende Lesevorgänge ermöglicht.

Der wesentliche mechanische Unterschied besteht darin, dass .__ gesendet, empfangen, verbunden und akzeptiert wird. kann zurückkehren, ohne etwas getan zu haben. Sie haben (natürlich) eine Nummer von Entscheidungen. Sie können den Rückgabecode und die Fehlercodes überprüfen und generell mach dich verrückt! Wenn Sie mir nicht glauben, versuchen Sie es einmal

Quelle: https://docs.python.org/2/howto/sockets.html

Es sagt auch weiter:

Es steht außer Frage, dass der schnellste Sockets-Code nicht blockierend ist Steckdosen und wählen Sie, um sie zu multiplexen. Sie können etwas zusammenstellen. Dies führt zu einer Sättigung einer LAN-Verbindung, ohne die .__ zu belasten. ZENTRALPROZESSOR. Das Problem ist, dass eine so geschriebene App nicht viel von alles andere - es muss bereit sein, Bytes überhaupt zu mischen mal.

Angenommen, Ihre App soll tatsächlich etwas mehr tun als dass das Einfädeln die optimale Lösung ist

Möchten Sie Ihrer Ansicht jedoch eine ganze Menge Komplexität hinzufügen, indem Sie eigene Threads erzeugen. Vor allem, wenn Gunicorn als async Arbeiter ?

Die verfügbaren asynchronen Worker basieren auf Greenlets (über Eventlet und Gevent). Greenlets sind eine Implementierung der Genossenschaft Multithreading für Python. Im Allgemeinen sollte eine Anwendung in der Lage sein diese Arbeiterklassen ohne Änderungen verwenden.

und

Einige Beispiele für Verhalten, die asynchrone Worker erfordern: Applications lange Sperranrufe tätigen (dh externe Webservices)

Um es kurz zu machen, ändern Sie nichts! Lass es einfach sein. Wenn Sie Änderungen vornehmen, lassen Sie das Zwischenspeichern ein. Erwägen Sie die Verwendung von Cache-control einer von Python-Requests-Entwicklern empfohlenen Erweiterung.

6
e4c5

Sie können grequests verwenden. Damit können andere Greenlets ausgeführt werden, während die Anforderung erfolgt. Es ist mit der requests-Bibliothek kompatibel und gibt ein requests.Response-Objekt zurück. Die Verwendung ist wie folgt:

import grequests

@app.route('/do', methods = ['POST'])
def do():
    result = grequests.map([grequests.get('slow api')])
    return result[0].content

Edit: Ich habe einen Test hinzugefügt und gesehen, dass sich die Zeit mit Grequests nicht verbessert hat, da Gunicorns Gevent-Worker bei der Initialisierung bereits Affen-Patches durchführt: https://github.com/benoitc/gunicorn/blob/master /gunicorn/workers/ggevent.py#L65

1
jerry