У меня есть веб-хостинг Flask без доступа к cron
командам.
Как я могу выполнять какую-нибудь функцию Python каждый час?
python
asynchronous
flask
cron
scheduled-tasks
РомаВальцер
источник
источник
flask
у вас былApp.runonce
илиApp.runForNseconds
вы могли бы переключаться междуschedule
и бегуном фляги, но это не так, поэтому единственный способ на данный момент использовать этоВы можете использовать его
APScheduler
в своем приложении Flask и запускать свои задания через его интерфейс: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()
источник
lambda
внутриatexit.register
?atexit.register
нужна функция для вызова. Если бы мы просто прошли,cron.shutdown(wait=False)
мы бы передали результат вызоваcron.shutdown
(что, вероятно, так и естьNone
). Поэтому вместо этого мы передаем функцию с нулевым аргументом, и вместо того, чтобы давать ей имя и использовать оператор,def shutdown(): cron.shutdown(wait=False)
иatexit.register(shutdown)
вместо этого мы регистрируем ее в строке сlambda:
(что является выражением функции с нулевым аргументом .)Я немного новичок в концепции планировщиков приложений, но то, что я нашел здесь для APScheduler v3.3.1 , немного отличается. Я считаю, что для новейших версий структура пакета, имена классов и т. Д. Изменились, поэтому я помещаю здесь свежее решение, которое я недавно сделал, интегрированное с базовым приложением Flask:
#!/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()
Я также оставляю этот Gist здесь , если у кого-то есть интерес к обновлениям для этого примера.
Вот несколько ссылок для будущих чтений:
источник
apscheduler.schedulers.background
, поскольку возможно, вы можете столкнуться с другими полезными сценариями для вашего приложения. С уважением.Вы можете попробовать использовать BackgroundScheduler APScheduler для интеграции интервального задания в ваше приложение Flask. Ниже приведен пример, в котором используются blueprint и 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()
Надеюсь, это поможет :)
Ссылка:
источник
В качестве простого решения вы можете добавить маршрут, например
@app.route("/cron/do_the_thing", methods=['POST']) def do_the_thing(): logging.info("Did the thing") return "OK", 200
Затем добавьте задание unix cron, которое периодически отправляет POST на эту конечную точку. Например, чтобы запускать его раз в минуту, в терминальном типе
crontab -e
и добавьте эту строку:(Обратите внимание, что путь к curl должен быть полным, поскольку при запуске задания у него не будет вашего PATH. Вы можете узнать полный путь к curl в вашей системе
which curl
)Мне это нравится тем, что легко протестировать задание вручную, в нем нет дополнительных зависимостей и, поскольку в нем нет ничего особенного, его легко понять.
Безопасность
Если вы хотите защитить паролем свое задание cron, вы можете
pip install Flask-BasicAuth
, а затем добавить учетные данные в конфигурацию своего приложения:app = Flask(__name__) app.config['BASIC_AUTH_REALM'] = 'realm' app.config['BASIC_AUTH_USERNAME'] = 'falken' app.config['BASIC_AUTH_PASSWORD'] = 'joshua'
Чтобы защитить паролем конечную точку задания:
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
Затем, чтобы вызвать его из своей работы cron:
источник
Другой альтернативой может быть использование Flask-APScheduler, который хорошо работает с Flask, например:
Больше информации здесь:
https://pypi.python.org/pypi/Flask-APScheduler
источник
Полный пример с использованием расписания и многопроцессорной обработки, с включением и выключением и параметром run_job (), коды возврата упрощены, а интервал установлен на 10 секунд, измененный на 2 часа
every(2).hour.do()
. График впечатляет, он не дрейфует, и я никогда не видел, чтобы он отклонялся более чем на 100 мс при планировании. Использование многопроцессорной обработки вместо многопоточности, поскольку она имеет метод завершения.#!/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)
Вы проверяете это, просто выдав:
$ 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
Каждые 10 секунд таймер будет выдавать на консоль сообщение таймера:
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
источник
Возможно, вы захотите использовать какой-либо механизм очереди с планировщиком, например планировщик RQ, или что-то более тяжелое, например Celery (скорее всего, излишек).
источник