Celerybeat:更改单个时区任务导致验证错误“无效时区”

问题描述 投票:0回答:1

芹菜--版本 5.1.2(太阳谐波)

django --版本 3.2.8

我有一个 celery 计划,其中有四个在不同时区运行的任务。我使用 nowfun 来设置时区,并在 settings.py 中设置了 CELERY_ENABLE_UTC = False 。我关注了这篇 SO 帖子的最高回复:Celerybeat - 每个任务不同的时区

请注意,我今天早上进行了此更改 - 我在没有这些设置的情况下运行了以前版本的代码。

目前,我将 celery 结果保存到 CELERY_RESULT_BACKEND = 'django-db'。

自从实施允许根据不同时区运行不同任务的更改以来,我在运行 celery -A backendbeat -l info 时收到错误。

虽然这是头和尾,但它超级长: 头:

[2021-10-29 07:29:36,059:INFO/MainProcess] 节拍:开始... [2021-10-29 07:29:36,067:错误/MainProcess] 无法添加条目 'celery.backend_cleanup' 到数据库计划: ValidationError(["时区 '' 无效"]). 内容:{'task':'celery.backend_cleanup','schedule':

      • (m/h/d/dM/MY)>, '选项': {'expire_seconds': 43200}}

尾巴:

django.core.exceptions.ValidationError: ["时区无效 ''"]

Celerybeat 挂在最后一条错误消息上,我必须用 ctrl + c 杀死它。

我进入 celery 并阅读了他们关于在时区相关设置更改时手动重置数据库的说明 - 该网站说:

$ python 管理.py shell

从 django_celery_beat.models 导入

PeriodicTaskPeriodicTask.objects.update(last_run_at=None)

然后我找到了一些文档,上面写着:

警告:如果您更改 Django TIME_ZONE 设置您的定期任务 时间表仍将基于旧时区。为了解决这个问题,你 必须重置每个定期任务的“上次运行时间”:

从 django_celery_beat.models 导入周期性任务,周期性任务

PeriodicTask.objects.all().update(last_run_at=None)

PeriodicTasks.changed()

请注意,这将重置状态,就好像定期任务以前从未运行过一样。

所以我认为导致问题的原因正是上面所说的 - 我更改了时区,并且时间表仍在旧的 UTC 时区上运行,因此我需要更新它,尽管我的时间表之前已经运行过,所以当我输入:

>>> PeriodicTask.objects.all().update(last_run_at=None)

我收到回复:

13

然后当我输入时:

>>> PeriodicTasks.changed()

我收到类型错误:

TypeError:changed() 缺少 1 个必需的位置参数: ‘实例’

所以我的问题是:

如何更新PeriodTask 和PeriodicTasks?我应该向PeriodicTasks.changed()传递什么参数?第一个命令的预期响应是13吗?

这是我的芹菜.py:

from __future__ import absolute_import, unicode_literals
import os
from celery import Celery
from django.conf import settings
from celery.schedules import crontab
import pytz
from datetime import datetime

os.environ.setdefault(
    'DJANGO_SETTINGS_MODULE',
    'backend.settings'
)

app = Celery(
    'backend'
)

app.config_from_object(
    settings,
    namespace='CELERY'
)

def uk_time():
     return datetime.now(pytz.timezone('Europe/London'))

def us_time():
    return datetime.now(pytz.timezone('EST'))

def jp_time():
    return datetime.now(pytz.timezone('Japan'))

# Celery Beat Settings
app.conf.beat_schedule={
    'generate_signals_london': {
        'task': 'signals.tasks.generate_signals',
        'schedule': crontab(
            minute=0,
            hour=8,
            nowfun=uk_time,
            day_of_week='1,2,3,4,5'
        ),
        'args': ('UK',),
    },

    'generate_signals_ny': {
        'task': 'signals.tasks.generate_signals',
        'schedule': crontab(
            minute=0,
            hour=7,
            nowfun=us_time,
            day_of_week='1,2,3,4,5'
        ),
        'args': ('NY',),
    },

    'generate_signals_nyse': {
        'task': 'signals.tasks.generate_signals',
        'schedule': crontab(
            minute=0,
            hour=9,
            nowfun=us_time,
            day_of_week='1,2,3,4,5'
        ),
        'args': ('NYSE',),
    },

    'generate_signals_asia': {
        'task': 'signals.tasks.generate_signals',
        'schedule': crontab(
            minute=0,
            hour=8,
            nowfun=jp_time,
            day_of_week='1,2,3,4,5'
        ),
        'args': ('JP',),
    },

}

app.autodiscover_tasks()
django timezone celery django-celery celerybeat
1个回答
-1
投票

有时,当尝试解决问题时,我们实际上是在解决因解决初始问题的错误方法而产生的问题。

如果答案是“这就是在更改时区时更新先前运行任务的周期性任务、周期性任务的方式”,那么最初的问题是什么?

这里最初的问题是更改不同任务的时区,以便这些任务合并不同时区的 DST。遵循 Celerybeat - 每个任务不同时区的解决方案并不是解决此问题的最有效方法。

为了避免需要更新PeriodicTask和PeriodicTasks,不要更改CELERY_ENABLE_UTC = False,而是按照这篇文章的答案以UTC运行所有内容:Celery计划任务与时区的问题

这解决了原来的问题。

这是我更新的 celery.py 和一个有效的解决方案:

import os
from django import setup
from celery import Celery
from celery.schedules import crontab
from datetime import datetime
import pytz
import logging

logging.basicConfig(level=logging.ERROR, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'api.settings')
setup()
app = Celery('api')
app.conf.timezone = 'UTC'

app.config_from_object('django.conf:settings', namespace='CELERY')
app.conf.broker_connection_retry_on_startup = True

EST = pytz.timezone('US/Eastern')
dt_obj = datetime.now(EST)

if bool(dt_obj.dst()):
    # GMT+3: UTC is 3 hours behind
    tokyo_bias_hour = 23
    london_bias_hour = 7
    ny_bias_hour = 11
    nyse_bias_hour = 15
    tokyo_market_open_hour = 0
    london_market_open_hour = 8
    ny_market_open_hour = 14
    nyse_market_open_hour = 16
    
else:
    # GMT+2: UTC is 2 hours behind
    tokyo_bias_hour = 22
    london_bias_hour = 6
    ny_bias_hour = 10
    nyse_bias_hour = 14
    tokyo_market_open_hour = 23
    london_market_open_hour = 7
    ny_market_open_hour = 13
    nyse_market_open_hour = 15

def current_timezone():
    EST = pytz.timezone('US/Eastern')
    dt_obj = datetime.now(EST)
    return 'Etc/GMT-3' if dt_obj.dst() else 'Etc/GMT-2'

def current_time():
    time_now = datetime.now(pytz.timezone(current_timezone()))
    logger.info(f"Current time is: {time_now}")
    return time_now

def get_datetime_str(hour, minute=0):
    current_dt = current_time()
    return current_dt.replace(hour=hour, minute=minute).strftime('%Y-%m-%dT%H:%M:%S')

# Set up the beat schedule
app.conf.beat_schedule = {
    'tokyo_bias': {
        'task': 'signals_app.tasks.process_signal_tasks',
        'schedule': crontab(hour=tokyo_bias_hour, minute=0, day_of_week='mon-fri'),
        'args': (get_datetime_str(0), 'Tokyo', 'market_open_bias')
    },
    'london_bias': {
        'task': 'signals_app.tasks.process_signal_tasks',
        'schedule': crontab(hour=london_bias_hour, minute=0, day_of_week='mon-fri'),
        'args': (get_datetime_str(8), 'London', 'market_open_bias')
    },
    'ny_bias': {
        'task': 'signals_app.tasks.process_signal_tasks',
        'schedule': crontab(hour=ny_bias_hour, minute=0, day_of_week='mon-fri'),
        'args': (get_datetime_str(12), 'NewYork', 'market_open_bias')
    },
    'nyse_bias': {
        'task': 'signals_app.tasks.process_signal_tasks',
        'schedule': crontab(hour=nyse_bias_hour, minute=0, day_of_week='mon-fri'),
        'args': (get_datetime_str(16), 'NYSE', 'market_open_bias')
    },
    'tokyo_market_open': {
        'task': 'signals_app.tasks.process_signal_tasks',
        'schedule': crontab(hour=tokyo_market_open_hour, minute=59, day_of_week='mon-fri'),
        'args': (get_datetime_str(1), 'Tokyo', 'market_open')
    },
    'london_market_open': {
        'task': 'signals_app.tasks.process_signal_tasks',
        'schedule': crontab(hour=london_market_open_hour, minute=0, day_of_week='mon-fri'),
        'args': (get_datetime_str(9), 'London', 'market_open')
    },
    'ny_market_open': {
        'task': 'signals_app.tasks.process_signal_tasks',
        'schedule': crontab(hour=ny_market_open_hour, minute=0, day_of_week='mon-fri'),
        'args': (get_datetime_str(15), 'NewYork', 'market_open')
    },
    'nyse_market_open': {
        'task': 'signals_app.tasks.process_signal_tasks',
        'schedule': crontab(hour=nyse_market_open_hour, minute=0, day_of_week='mon-fri'),
        'args': (get_datetime_str(17), 'NYSE', 'market_open')
    },
}

app.autodiscover_tasks()
© www.soinside.com 2019 - 2024. All rights reserved.