How to schedule a function to run every hour on Flask?

前端 未结 9 2170
我寻月下人不归
我寻月下人不归 2020-11-28 01:10

I have a Flask web hosting with no access to cron command.

How can I execute some Python function every hour?

相关标签:
9条回答
  • 2020-11-28 01:23

    For a simple solution, you could add a route such as

    @app.route("/cron/do_the_thing", methods=['POST'])
    def do_the_thing():
        logging.info("Did the thing")
        return "OK", 200
    

    Then add a unix cron job that POSTs to this endpoint periodically. For example to run it once a minute, in terminal type crontab -e and add this line:

    * * * * * /opt/local/bin/curl -X POST https://YOUR_APP/cron/do_the_thing
    

    (Note that the path to curl has to be complete, as when the job runs it won't have your PATH. You can find out the full path to curl on your system by which curl)

    I like this in that it's easy to test the job manually, it has no extra dependencies and as there isn't anything special going on it is easy to understand.

    Security

    If you'd like to password protect your cron job, you can pip install Flask-BasicAuth, and then add the credentials to your app configuration:

    app = Flask(__name__)
    app.config['BASIC_AUTH_REALM'] = 'realm'
    app.config['BASIC_AUTH_USERNAME'] = 'falken'
    app.config['BASIC_AUTH_PASSWORD'] = 'joshua'
    

    To password protect the job endpoint:

    from flask_basicauth import BasicAuth
    basic_auth = BasicAuth(app)
    
    @app.route("/cron/do_the_thing", methods=['POST'])
    @basic_auth.required
    def do_the_thing():
        logging.info("Did the thing a bit more securely")
        return "OK", 200
    

    Then to call it from your cron job:

    * * * * * /opt/local/bin/curl -X POST https://falken:joshua@YOUR_APP/cron/do_the_thing
    
    0 讨论(0)
  • 2020-11-28 01:25

    A complete example using schedule and multiprocessing, with on and off control and parameter to run_job() the return codes are simplified and interval is set to 10sec, change to every(2).hour.do()for 2hours. Schedule is quite impressive it does not drift and I've never seen it more than 100ms off when scheduling. Using multiprocessing instead of threading because it has a termination method.

    #!/usr/bin/env python3
    
    import schedule
    import time
    import datetime
    import uuid
    
    from flask import Flask, request
    from multiprocessing import Process
    
    app = Flask(__name__)
    t = None
    job_timer = None
    
    def run_job(id):
        """ sample job with parameter """
        global job_timer
        print("timer job id={}".format(id))
        print("timer: {:.4f}sec".format(time.time() - job_timer))
        job_timer = time.time()
    
    def run_schedule():
        """ infinite loop for schedule """
        global job_timer
        job_timer = time.time()
        while 1:
            schedule.run_pending()
            time.sleep(1)
    
    @app.route('/timer/<string:status>')
    def mytimer(status, nsec=10):
        global t, job_timer
        if status=='on' and not t:
            schedule.every(nsec).seconds.do(run_job, str(uuid.uuid4()))
            t = Process(target=run_schedule)
            t.start()
            return "timer on with interval:{}sec\n".format(nsec)
        elif status=='off' and t:
            if t:
                t.terminate()
                t = None
                schedule.clear()
            return "timer off\n"
        return "timer status not changed\n"
    
    if __name__ == '__main__':
        app.run(host='0.0.0.0', port=5000)
    

    You test this by just issuing:

    $ curl http://127.0.0.1:5000/timer/on
    timer on with interval:10sec
    $ curl http://127.0.0.1:5000/timer/on
    timer status not changed
    $ curl http://127.0.0.1:5000/timer/off
    timer off
    $ curl http://127.0.0.1:5000/timer/off
    timer status not changed
    

    Every 10sec the timer is on it will issue a timer message to console:

    127.0.0.1 - - [18/Sep/2018 21:20:14] "GET /timer/on HTTP/1.1" 200 -
    timer job id=b64ed165-911f-4b47-beed-0d023ead0a33
    timer: 10.0117sec
    timer job id=b64ed165-911f-4b47-beed-0d023ead0a33
    timer: 10.0102sec
    
    0 讨论(0)
  • 2020-11-28 01:26

    You could make use of APScheduler in your Flask application and run your jobs via its interface:

    import atexit
    
    # v2.x version - see https://stackoverflow.com/a/38501429/135978
    # for the 3.x version
    from apscheduler.scheduler import Scheduler
    from flask import Flask
    
    app = Flask(__name__)
    
    cron = Scheduler(daemon=True)
    # Explicitly kick off the background thread
    cron.start()
    
    @cron.interval_schedule(hours=1)
    def job_function():
        # Do your work here
    
    
    # Shutdown your cron thread if the web process is stopped
    atexit.register(lambda: cron.shutdown(wait=False))
    
    if __name__ == '__main__':
        app.run()
    
    0 讨论(0)
  • 2020-11-28 01:29

    You can use BackgroundScheduler() from APScheduler package (v3.5.3):

    import time
    import atexit
    
    from apscheduler.schedulers.background import BackgroundScheduler
    
    
    def print_date_time():
        print(time.strftime("%A, %d. %B %Y %I:%M:%S %p"))
    
    
    scheduler = BackgroundScheduler()
    scheduler.add_job(func=print_date_time, trigger="interval", seconds=3)
    scheduler.start()
    
    # Shut down the scheduler when exiting the app
    atexit.register(lambda: scheduler.shutdown())
    

    Note that two of these schedulers will be launched when Flask is in debug mode. For more information, check out this question.

    0 讨论(0)
  • 2020-11-28 01:32

    I'm a little bit new with the concept of application schedulers, but what I found here for APScheduler v3.3.1 , it's something a little bit different. I believe that for the newest versions, the package structure, class names, etc., have changed, so I'm putting here a fresh solution which I made recently, integrated with a basic Flask application:

    #!/usr/bin/python3
    """ Demonstrating Flask, using APScheduler. """
    
    from apscheduler.schedulers.background import BackgroundScheduler
    from flask import Flask
    
    def sensor():
        """ Function for test purposes. """
        print("Scheduler is alive!")
    
    sched = BackgroundScheduler(daemon=True)
    sched.add_job(sensor,'interval',minutes=60)
    sched.start()
    
    app = Flask(__name__)
    
    @app.route("/home")
    def home():
        """ Function for test purposes. """
        return "Welcome Home :) !"
    
    if __name__ == "__main__":
        app.run()
    

    I'm also leaving this Gist here, if anyone have interest on updates for this example.

    Here are some references, for future readings:

    • APScheduler Doc: https://apscheduler.readthedocs.io/en/latest/
    • daemon=True: https://docs.python.org/3.4/library/threading.html#thread-objects
    0 讨论(0)
  • 2020-11-28 01:37

    You could try using APScheduler's BackgroundScheduler to integrate interval job into your Flask app. Below is the example that uses blueprint and app factory (init.py) :

    from datetime import datetime
    
    # import BackgroundScheduler
    from apscheduler.schedulers.background import BackgroundScheduler
    from flask import Flask
    
    from webapp.models.main import db 
    from webapp.controllers.main import main_blueprint    
    
    # define the job
    def hello_job():
        print('Hello Job! The time is: %s' % datetime.now())
    
    def create_app(object_name):
        app = Flask(__name__)
        app.config.from_object(object_name)
        db.init_app(app)
        app.register_blueprint(main_blueprint)
        # init BackgroundScheduler job
        scheduler = BackgroundScheduler()
        # in your case you could change seconds to hours
        scheduler.add_job(hello_job, trigger='interval', seconds=3)
        scheduler.start()
    
        try:
            # To keep the main thread alive
            return app
        except:
            # shutdown if app occurs except 
            scheduler.shutdown()
    

    Hope it helps :)

    Ref :

    1. https://github.com/agronholm/apscheduler/blob/master/examples/schedulers/background.py
    0 讨论(0)
提交回复
热议问题