APScheduler进程重启后错过的Cron任务未执行,求排查解决
问题:APScheduler错过的Cron任务重启后未执行
APScheduler版本:3.10.4
我做了如下测试:
- 设置一个30秒后执行的Cron任务
- 终止调度器进程,等待1-2分钟后重启
- 预期行为:错过的任务在进程重启后立即执行
实际情况:
- Redis服务器全程运行,已通过
redis-cli确认任务状态已持久化 - 使用SQLAlchemy存储方案也复现了相同问题
- 重启后任务完全未执行,始终处于错过状态
上线前必须解决该错过任务的恢复问题,以下是可复现问题的clock.py脚本:
# apscheduler version 3.10.4 import logging import os import sys import urllib.parse from apscheduler.executors.pool import ThreadPoolExecutor from apscheduler.jobstores.redis import RedisJobStore from apscheduler.schedulers.blocking import BlockingScheduler from pytz import utc # Without this, the paths to `snake` modules etc. will not be located. sys.path.append(os.path.join(os.path.dirname(__file__), "mysite")) import django django.setup() from django.conf import settings logging.basicConfig() # The string "apscheduler" is necessary for logs to give good output. logging.getLogger('apscheduler').setLevel(logging.DEBUG) # WEB_CONCURRENCY is set by Heroku dynos automatically number_of_cores = int(os.environ.get("WEB_CONCURRENCY", os.cpu_count() or 1)) parsed_redis_url = urllib.parse.urlparse(settings.REDIS_URL) redis_job_store = RedisJobStore( host=parsed_redis_url.hostname, port=parsed_redis_url.port, password=parsed_redis_url.password, db=settings.CACHE_REDIS_DB, ssl=True if parsed_redis_url.scheme == "rediss" else False, ssl_cert_reqs=None, ) # ========== ## APScheduler Configuration job_defaults = { 'max_instances': 1, 'misfire_grace_time': 60 * 50, 'coalesce': True } executors = {'default': ThreadPoolExecutor(number_of_cores)} jobstores = { 'default': redis_job_store, } scheduler = BlockingScheduler( executors=executors, logger=logging, job_defaults=job_defaults, timezone=utc, jobstores=jobstores, ) @scheduler.scheduled_job('cron', hour=20, minute=38, second=40, name="print_ongoing", id="1") def print_ongoing(): from time import sleep from django.utils.crypto import get_random_string unique_id = get_random_string(length=4) s = 0 while s < 1000: print(unique_id) s += 1 sleep(1) scheduler.start()
此外,Redis中序列化后的任务内容如下:
{ 'version': 1, 'id': '1', 'func': '__main__:print_ongoing', 'trigger': { '__type__': 'apscheduler.triggers.cron.CronTrigger', 'timezone': 'pytz._UTC', 'start_date': None, 'end_date': None, 'fields': [ { '__type__': 'apscheduler.triggers.cron.fields.BaseField', 'name': 'year', 'is_default': True, 'expressions': [ { '__type__': 'apscheduler.triggers.cron.expressions.AllExpression', 'step': None } ] }, { '__type__': 'apscheduler.triggers.cron.fields.MonthField', 'month': None, 'range': None, 'expressions': [ { 'day': None, 'range': None } ] }, { '__type__': 'apscheduler.triggers.cron.fields.DayOfMonthField', 'day': None, 'range': None, 'expressions': [ { 'day': None, 'range': None } ] }, { '__type__': 'apscheduler.triggers.cron.fields.WeekField', 'week': None, 'range': None, 'expressions': [ { 'week': None, 'range': None } ] }, { '__type__': 'apscheduler.triggers.cron.fields.DayOfWeekField', 'day_of_week': None, 'range': None, 'expressions': [ { 'day_of_week': None, 'range': None } ] }, { 'hour': None, 'range': { 'first': 20, 'last': 20 } }, { 'minute': None, 'range': { 'first': 20, 'last': 20 } }, { 'second': None, 'range': { 'first': 20, 'last': 20 } } ] }, 'jitter': None, 'executor': 'default', 'args': None, 'kwargs': { 'print_ongoing': None }, 'misfire_grace_time': 3000, 'coalesce': True, 'max_instances': 1, 'next_run_time': '2024-02-16 00:20:00' }
内容的提问来源于stack exchange,提问作者Jack Kinsella
相关产品推荐
相关产品推荐

