web-dev-qa-db-fra.com

Faire une requête non bloquante avec des requêtes lors de l'exécution de Flask avec Gunicorn et Gevent

Mon application Flask recevra une demande, effectuera certains traitements, puis adressera une demande à un point de terminaison externe lent nécessitant 5 secondes pour répondre. Il semble que faire fonctionner Gunicorn avec Gevent lui permettra de gérer plusieurs de ces demandes lentes en même temps. Comment puis-je modifier l'exemple ci-dessous afin que la vue ne soit pas bloquante?

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

Si vous déployez votre application Flask avec gunicorn, elle est déjà non bloquante. Si un client attend une réponse de l'une de vos vues, un autre client peut faire une demande à la même vue sans problème. Il y aura plusieurs travailleurs pour traiter plusieurs demandes simultanément. Pas besoin de changer votre code pour que cela fonctionne. Cela vaut également pour pratiquement chaque option de déploiement Flask.

10
sytech

Tout d’abord, un peu d’arrière-plan, une socket bloquante est le type de socket par défaut. Dès que vous commencez à lire votre application ou votre thread, vous ne reprenez pas le contrôle tant que les données ne sont pas réellement lues ou que vous n'êtes pas connecté. Voici comment python-requests fonctionne par défaut. Il existe un spin-off appelé grequests qui fournit des lectures non bloquantes.

La différence mécanique majeure est que envoyer, recevoir, connecter et accepter peut revenir sans avoir rien fait. Vous avez (bien sûr) un numéro de choix. Vous pouvez vérifier le code de retour et les codes d'erreur et généralement vous rendre fou. Si vous ne me croyez pas, essayez-le un jour

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

Il est également dit:

Il n’est pas question que le code de socket le plus rapide utilise un système non bloquant prises et sélectionnez pour les multiplexer. Vous pouvez mettre en place quelque chose cela saturera une connexion LAN sans exercer de contrainte sur le CPU. Le problème, c’est qu’une application écrite de cette façon ne peut pas faire grand-chose de toute autre chose - il doit être prêt à mélanger les octets du tout fois.

En supposant que votre application soit réellement supposée faire quelque chose de plus que que le filetage est la solution optimale

Mais voulez-vous ajouter beaucoup de complexité à votre vue en l’affichant dans ses propres threads? Surtout quand gunicorn en tant que travailleurs asynchrones ?

Les travailleurs asynchrones disponibles sont basés sur Greenlets (via Eventlet et Gevent). Les Greenlets sont une implémentation de la coopérative multi-threading pour Python. En général, une application devrait pouvoir faire usage de ces classes de travail sans modifications.

et

Quelques exemples de comportement nécessitant des travailleurs asynchrones: Applications faire des appels de blocage longs (par exemple, services Web externes)

Donc, pour faire court, ne changez rien! Juste le laisser être. Si vous apportez des modifications, laissez-le être pour introduire la mise en cache. Pensez à utiliser Cache-control une extension recommandée par les développeurs de requêtes python.

6
e4c5

Vous pouvez utiliser grequests. Cela permet aux autres greenlets de s'exécuter pendant que la requête est faite. Il est compatible avec la bibliothèque requests et renvoie un objet requests.Response. L'utilisation est la suivante:

import grequests

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

Edit: J'ai ajouté un test et constaté que le temps ne s'était pas amélioré avec les demandes de crédits, car le travailleur de gicent de gunicorn effectue déjà le patch-singe lors de son initialisation: https://github.com/benoitc/gunicorn/blob/master /gunicorn/workers/ggevent.py#L65

1
jerry