web-dev-qa-db-fra.com

Comment exécuter une tâche récurrente dans le framework Python Flask?

Je construis un site Web qui fournit des informations aux visiteurs. Ces informations sont agrégées en arrière-plan en interrogeant quelques API externes toutes les 5 secondes. La façon dont je le fais fonctionner maintenant est que j'utilise APScheduler jobs. J'ai d'abord préféré APScheduler car il rend l'ensemble du système plus facile à porter (car je n'ai pas besoin de définir de tâches cron sur la nouvelle machine). Je lance les fonctions d'interrogation comme suit:

from apscheduler.scheduler import Scheduler

@app.before_first_request
def initialize():
    apsched = Scheduler()
    apsched.start()

    apsched.add_interval_job(checkFirstAPI, seconds=5)
    apsched.add_interval_job(checkSecondAPI, seconds=5)
    apsched.add_interval_job(checkThirdAPI, seconds=5)

Cela fonctionne un peu, mais il y a quelques problèmes:

  1. Pour commencer, cela signifie que les travaux d'intervalle s'exécutent en dehors du contexte Flask. Jusqu'à présent, cela n'a pas été un gros problème, mais lorsque l'appel d'un point de terminaison échoue, je veux que le système envoyez-moi un e-mail (en disant "Hé appel à l'API X a échoué"). Parce qu'il ne fonctionne pas dans le contexte Flask cependant, il se plaint que flask-mail ne peut pas être exécuté (RuntimeError('working outside of application context')).
  2. Deuxièmement, je me demande comment cela va se passer quand je n'utilise plus le serveur de débogage intégré Flask, mais un serveur de production avec, disons, 4 ouvriers. Va-t-il démarrer chaque tâche quatre fois alors?

Dans l'ensemble, je pense qu'il devrait y avoir une meilleure façon d'exécuter ces tâches récurrentes, mais je ne sais pas comment. Quelqu'un a-t-il une solution intéressante à ce problème? Tous les conseils sont les bienvenus!

[EDIT] Je viens de lire sur Céleri avec ses horaires . Bien que je ne vois pas vraiment en quoi le céleri est différent d'APScheduler et s'il pourrait ainsi résoudre mes deux points, je me demande si quelqu'un qui lit ceci pense que je devrais enquêter davantage sur le céleri?

[CONCLUSION] Environ deux ans plus tard, je lis ceci, et je pensais que je pourrais vous faire savoir avec quoi je me suis retrouvé. J'ai pensé que @BluePeppers avait raison de dire que je ne devrais pas être lié aussi étroitement à l'écosystème Flask. J'ai donc opté pour des tâches cron régulières exécutées chaque minute qui sont définies à l'aide d'Ansible. cela le rend un peu plus complexe (j'avais besoin d'apprendre Ansible et de convertir du code pour que le faire fonctionner toutes les minutes soit suffisant) Je pense que c'est plus robuste. J'utilise actuellement le génial pythonr-rq = pour la mise en file d'attente des tâches de synchronisation (vérification des API et envoi d'e-mails). Je viens de découvrir rq-scheduler . Je ne l'ai pas encore testé, mais il semble faire exactement ce dont j'avais besoin dans le C'est peut-être un conseil pour les futurs lecteurs de cette question.

Pour le reste, je vous souhaite à tous une belle journée!

26
kramer65

(1)

Vous pouvez utiliser le gestionnaire de contexte app.app_context() pour définir le contexte de l'application. J'imagine que l'utilisation irait quelque chose comme ceci:

from apscheduler.scheduler import Scheduler

def checkSecondApi():
    with app.app_context():
        # Do whatever you were doing to check the second API

@app.before_first_request
def initialize():
    apsched = Scheduler()
    apsched.start()

    apsched.add_interval_job(checkFirstAPI, seconds=5)
    apsched.add_interval_job(checkSecondAPI, seconds=5)
    apsched.add_interval_job(checkThirdAPI, seconds=5)

Alternativement, vous pouvez utiliser un décorateur

def with_application_context(app):
    def inner(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            with app.app_context():
                return func(*args, **kwargs)
        return wrapper
    return inner

@with_application_context(app)
def checkFirstAPI():
    # Check the first API as before

(2)

Oui, cela fonctionnera toujours. La seule différence (significative) est que votre application ne communiquera pas directement avec le monde; il passera par un proxy inverse ou quelque chose via fastcgi/uwsgi/peu importe. La seule préoccupation est que si vous avez plusieurs instances du démarrage de l'application, plusieurs planificateurs seront créés. Pour gérer cela, je vous suggère de déplacer vos tâches back-end hors de l'application Flask, et d'utiliser un outil conçu pour exécuter des tâches régulièrement (par exemple Celery). L'inconvénient est que vous avez gagné " t pouvoir utiliser des choses comme Flask-Mail, mais imo, ce n'est pas trop bon d'être si étroitement lié à l'écosystème Flask; que gagnez-vous en utilisant Flask-Mail sur une norme, non Flask, bibliothèque de courrier?

En outre, la décomposition de votre application facilite considérablement la mise à l'échelle de composants individuels, car la capacité est requise, par rapport à une application Web monolithique.

24
BluePeppers