Я пытаюсь запланировать несколько заданий внутри моего python. Предположительно, текст из журнала должен появляться каждые 1 минуту и каждые 5 минут из файла jobs.py внутри моего контейнера докеров. Однако текст появляется каждые 2 минуты внутри док-контейнера. Есть ли конфликт между расписанием python и cronjobs?
Текущий вывод внутри док-контейнера
13:05:00 [I] werkzeug 172.20.0.2 - - [08/May/2022 13:05:00] "GET /reminder/send_reminders HTTP/1.1" 200 -
13:06:00 [I] werkzeug 172.20.0.2 - - [08/May/2022 13:06:00] "GET /feeds/update_feeds HTTP/1.1" 200 -
13:07:00 [D] schedule Running job Job(interval=1, unit=minutes, do=job_feeds_update, args=(), kwargs = {})
13:07:00 [I] jobs job_feeds_update
13:07:00 [I] werkzeug 172.20.0.2 - - [08/May/2022 13:07:00] "GET /feeds/update_feeds HTTP/1.1" 200 -
13:08:00 [I] werkzeug 172.20.0.2 - - [08/May/2022 13:08:00] "GET /feeds/update_feeds HTTP/1.1" 200 -
13:09:00 [D] schedule Running job Job(interval=1, unit=minutes, do=job_feeds_update, args=(), kwargs = {})
13:09:00 [I] jobs job_feeds_update
13:09:00 [I] werkzeug 172.20.0.2 - - [08/May/2022 13:09:00] "GET /feeds/update_feeds HTTP/1.1" 200 -
13:10:00 [I] werkzeug 172.20.0.2 - - [08/May/2022 13:10:00] "GET /feeds/update_feeds HTTP/1.1" 200 -
13:10:00 [I] werkzeug 172.20.0.2 - - [08/May/2022 13:10:00] "GET /reminder/send_reminders HTTP/1.1" 200 -
13:11:00 [D] schedule Running job Job(interval=1, unit=minutes, do=job_feeds_update, args=(), kwargs = {})
13:11:00 [I] jobs job_feeds_update
13:11:00 [D] schedule Running job Job(interval=5, unit=minutes, do=job_send_reminders, args=(), kwargs = {})
13:11:00 [I] jobs job_send_reminders
сервер.py
#Cron Job
@app.route('/feeds/update_feeds')
def update_feeds():
schedule.run_pending()
return 'OK UPDATED FEED!'
@app.route('/reminder/send_reminders')
def send_reminders():
schedule.run_pending()
return 'OK UPDATED STATUS!'
рабочие места.py
def job_feeds_update():
update_feed()
update_feed_eng()
logger.info("job_feeds_update")
schedule.every(1).minutes.do(job_feeds_update)
# send email reminders
def job_send_reminders():
send_reminders()
logger.info("job_send_reminders")
schedule.every(5).minutes.do(job_send_reminders)
Докер-файл
FROM alpine:latest
# Install curlt
RUN apk add --no-cache curl
# Copy Scripts to Docker Image
COPY reminders.sh /usr/local/bin/reminders.sh
COPY feeds.sh /usr/local/bin/feeds.sh
RUN echo ' */5 * * * * /usr/local/bin/reminders.sh' >> /etc/crontabs/root
RUN echo ' * * * * * /usr/local/bin/feeds.sh' >> /etc/crontabs/root
# Run crond -f for Foreground
CMD ["/usr/sbin/crond", "-f"]
Напоминания просто запускают «/reminder/send_reminders», а каналы запускают «/feeds/update_feeds»? Также когда выполняется jobs.py?
@Azaro да, это запускает маршруты соответственно
Попался - когда job.py запускается/инициализируется, кстати?
Вы имеете в виду, когда jobs.py запускается внутри контейнера докеров?
Да, для этого есть отдельный dockerfile? Просто любопытно - я не думаю, что с этим что-то не так. Кроме того, чтобы помочь нам получить больше информации, можете ли вы добавить logger.info("job_feeds_update started") в начало вашего метода job_feeds_update() (и сделать то же самое для журнала методов задания напоминаний), чтобы мы могли получить четкую временную метку того, когда задания действительно выполняются!
Итак, у меня было две папки в моих проектах. Я создал файл dockerfile для каталога приложений, job.py находится внутри каталога приложений и файл dockerfile для каталога cronjob. В настоящее время я использую docker compose для запуска двух сервисов для своих проектов. Хорошо, я добавлю информацию о регистраторе и опубликую последнюю отметку времени.
Звучит хорошо, спасибо. Я подозреваю, что проблема может быть связана с тем, что schedule не учитывается, сколько времени занимает каждое задание, и, таким образом, нарушается синхронизация расписания.
я добавил logger.info("job_feeds_updated started ") в начало job_feeds_update(), но он не показал результат.
вывод не проблема? кажется, что расписание не синхронизировано, поэтому мне интересно, правильно это или неправильно
Мне странно, что «начатый» журнал не печатается, а другой журнал печатается в конце функции. Но в любом случае у меня есть некоторые мысли о потенциальной проблеме. Попытка проверить вещи локально, но дайте мне немного!
Хорошо !! с нетерпением ждем ответа от вас.
Я опубликовал (длинное) объяснение. У меня есть несколько предложений о том, как вы можете решить свою проблему, но я еще не добавил их в пост. Я подумал, что вы можете прочитать объяснение, пока я продолжаю редактировать свой пост с решениями
Также добавлено несколько возможных решений. Обратите внимание, что с вашей стороны потребуются пробы и ошибки, чтобы убедиться, что все работает должным образом, когда вы тестируете всю установку!
Я буду читать его и занять некоторое время, чтобы обработать информацию. Я сообщу вам о пробной/ошибочной части, чтобы убедиться, что все работает.
Отлично, надеюсь, что это в конечном итоге поможет!
спасибо, что уделили время и подробно все описали. Я свяжусь с вами, если у меня возникнут проблемы во время реализации
Без проблем! Обратите внимание, я только что добавил дополнительное возможное решение, о котором я только что узнал: модуль pypi.org/проект/ischeduleischedule, который работает как расписание, но на самом деле учитывает время выполнения задания. Это может быть самым простым решением для запуска, если очередей/потоков слишком много!
Просто хотел продолжить и посмотреть, помогло ли это в конечном итоге!
@Azarro, использующий ischedule, помогает поддерживать согласованное время выполнения задания и планирование. большое спасибо!!






Я думаю, вы столкнулись с несколькими проблемами:
schedule находится в другом расписании/интервале, чем ваша работа cron. Они не синхронизированы (и вы никогда не можете ожидать, что они будут синхронизированы по следующей причине). С момента выполнения вашего скрипта jobs.py это начальная точка, от которой расписание отсчитывает интервалы.то есть, если вы запускаете что-то каждую минуту, но скрипт jobs.py запускается через 30 секунд текущей минуты (т.е. 01:00:30 - 1:00 через 30 секунд), то планировщик запустит задание в 1:01:30. , затем 1:02:30, затем 1:03:30 и так далее.
Schedule Частотное исполнение не гарантирует вам точности. Когда планировщик запускает задание, время выполнения задания не учитывается. Поэтому, если вы запланируете что-то вроде заданий на рассылку/напоминания, это может занять некоторое время. После завершения выполнения планировщик решает, что следующее задание будет выполняться только 1 минуту после окончания предыдущей работы. Это означает, что ваше время выполнения может нарушить график.Попробуйте запустить этот пример в скрипте Python, чтобы понять, о чем я говорю.
# Schedule Library imported
import schedule
import time
from datetime import datetime
def geeks():
now = datetime.now() # current date and time
date_time = now.strftime("%m/%d/%Y, %H:%M:%S")
time.sleep(5)
print(date_time + "- Look at the timestamp")
geeks();
# Task scheduling
# After every 10mins geeks() is called.
schedule.every(1).seconds.do(geeks)
# Loop so that the scheduling task
# keeps on running all time.
while True:
# Checks whether a scheduled task
# is pending to run or not
schedule.run_pending()
time.sleep(0.1)
Мы запланировали запуск функции geeks каждую секунду. Но если вы посмотрите на функцию гиков, я добавил time.sleep(5), чтобы представить, что здесь может быть какой-то блокирующий вызов API, который может занять 5 секунд. Затем посмотрите на записанные временные метки — вы заметите, что они не всегда соответствуют расписанию, которое мы изначально хотели!
Теперь о том, как ваша работа cron и планировщик не синхронизированы.
Посмотрите на следующие журналы:
13:07:00 [D] schedule Running job Job(interval=1, unit=minutes, do=job_feeds_update, args=(), kwargs = {})
13:07:00 [I] jobs job_feeds_update
13:07:00 [I] werkzeug 172.20.0.2 - - [08/May/2022 13:07:00] "GET /feeds/update_feeds HTTP/1.1" 200 -
# minute 8 doesn't trigger the schedule for feeds
13:09:00 [D] schedule Running job Job(interval=1, unit=minutes, do=job_feeds_update, args=(), kwargs = {})
13:09:00 [I] jobs job_feeds_update
13:09:00 [I] werkzeug 172.20.0.2 - - [08/May/2022 13:09:00] "GET /feeds/update_feeds HTTP/1.1" 200 -
Вероятно, здесь происходит следующее:
в 13:07:00 ваш cron отправляет запрос на подачу элементов
в 13:07:00 в расписании заданий есть ожидающее задание для элементов фида
в 13:07:00: задание завершается, и расписание определяет, что следующее задание может быть запущено только через 1 минуту, что составляет примерно ~13:08:01 (обратите внимание на 01, это для учета миллисекунд/времени задания). выполнений, что позволяет предположить, что для запуска обновления элементов фида потребовалась 1 секунда)
в 13:08:00 ваше задание cron запускает запрос, запрашивающий задания schedule run_pending.
в 13:08:00 Однако, есть нет ожидающих выполнения заданий, потому что следующий раз, когда элементы ленты могут запускаться, будет 13:08:01, а не прямо сейчас.
в 13:09:00 ваша вкладка cron снова запускает запрос
в 13:09:00 доступно ожидающее задание, которое должно было выполняться в 13:08:01, чтобы оно было выполнено сейчас.
Я надеюсь, что это иллюстрирует проблему, с которой вы столкнулись, из-за рассинхронизации между cron и расписанием. Эта проблема усугубится в производственной среде. Вы можете прочитать больше о Параллельное исполнение для schedule как о средстве отвлечься от основного потока, но это пока не так далеко. Давайте поговорим о...
run_all из расписания вместо run_pending, чтобы принудительно запускать задания, независимо от того, когда они фактически запланированы.Но если подумать, это ничем не отличается от простого вызова job_feeds_update прямо из вашего маршрута API. Это неплохая идея сама по себе, но она все еще не очень чистая, поскольку она будет блокировать основной поток вашего API-сервера до тех пор, пока job_feeds_update не будет завершен, что может быть не идеальным, если у вас есть другие маршруты, которые нужны пользователям.
Вы можете объединить это со следующим предложением:
Поскольку вы запускаете schedule.run_pending(), ваш основной поток на вашем сервере заблокирован до тех пор, пока не будут запущены задания. Используя потоки (+ очередь заданий), вы можете планировать задания в очереди + избегать блокировки основного сервера своими заданиями. Это должно немного оптимизировать работу для вас, позволяя планировать задания.
Вместо этого используйте ischedule, поскольку он учитывает время выполнения задания и предоставляет точные расписания: https://pypi.org/project/ischedule/. Это может быть простейшее решение для вас на случай, если 1+2 станет головной болью!
Не использовать расписание, и ваши задания cron просто попадут на маршрут, который просто запускает реальную функцию (так что в основном это противоречит совету использовать 1 + 2 выше). Проблема заключается в том, что если вашим функциям требуется больше минуты для обновления веб-канала, у вас может быть несколько перекрывающихся заданий cron, выполняющихся одновременно для обновления веб-канала. Поэтому я бы рекомендовал не делать этого и полагаться на механизм для постановки в очередь/планирования ваших запросов с потоками и заданиями. Только упоминая об этом как о потенциальном сценарии того, что еще вы могли бы сделать.
если бы кто-нибудь мог мне помочь, я был бы признателен!