Celery beat:更改为导致验证错误的个别时区任务 "Invalid timezone"

Celery beat: Change to individual timezone tasks causing validation error "Invalid timezone"

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

django --version 3.2.8

我有一个 celery 日程表,其中有四个 运行 在不同时区的任务。我正在使用 nowfun 设置时区,并在 settings.py 中设置了 CELERY_ENABLE_UTC = False。我关注了这个 SO post 的最高回复:Celery beat - different time zone per task

请注意,我今天早上进行了此更改 - 我 运行使用没有这些设置的旧版本代码。

目前,我正在将芹菜结果保存到 CELERY_RESULT_BACKEND = 'django-db'。

自从实施允许不同任务根据不同时区 运行 的更改后,当我 运行 celery -A backend beat -l info.

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

[2021-10-29 07:29:36,059: INFO/MainProcess] beat: Starting... [2021-10-29 07:29:36,067: ERROR/MainProcess] Cannot add entry 'celery.backend_cleanup' to database schedule: ValidationError(["Invalid timezone '<LocalTimezone: UTC+00>'"]). Contents: {'task': 'celery.backend_cleanup', 'schedule': <crontab: 0 4

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

尾部:

django.core.exceptions.ValidationError: ["Invalid timezone '<LocalTimezone: UTC+00>'"]

Celery beat 挂在最后一条错误消息上,我必须用 ctrl + c 将其杀死。

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

$ python manage.py shell

from django_celery_beat.models import

PeriodicTask PeriodicTask.objects.update(last_run_at=None)

然后我发现一些 documentation 说:

Warning: If you change the Django TIME_ZONE setting your periodic task schedule will still be based on the old timezone. To fix that you would have to reset the “last run time” for each periodic task:

from django_celery_beat.models import PeriodicTask, PeriodicTasks

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

PeriodicTasks.changed()

Note that this will reset the state as if the periodic tasks have never run before.

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

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

我收到回复:

13

然后当我输入时:

>>> PeriodicTasks.changed()

我收到类型错误:

TypeError: changed() missing 1 required positional argument: 'instance'

所以我的问题是:

如何更新 PeriodTask 和 PeriodicTasks?我应该将哪些参数传递给 PeriodicTasks.changed() 并且 13 是第一个命令的预期响应?

这是我的 celery.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()

有时,当我们试图解决一个问题时,我们实际上是在解决一个由不正确的解决初始问题的方法造成的问题。

如果答案是“这就是您在更改时区时更新 PeriodicTask、以前 运行 任务的 PeriodicTasks 的方式”那么原始问题是什么?

这里最初的问题是更改不同任务的时区,以便这些任务包含不同时区的夏令时。按照 Celery beat - different time zone per task 上的解决方案并不是解决此问题的最有效方法。

为避免需要更新 PeriodicTask 和 PeriodicTasks,请不要更改 CELERY_ENABLE_UTC = False 而是 运行 根据此 post 的答案 运行 UTC 中的所有内容:Celery scheduled tasks problems with Timezone

这解决了原来的问题。

这是我更新后的 celery.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'
)

dt = datetime.now()
tz = pytz.timezone('Europe/London')
dst = tz.localize(dt)

if bool(dst.dst()):
    uk_market_open = 9
else:
    uk_market_open = 8

tz = pytz.timezone('US/Eastern')
dst = tz.localize(dt)

if bool(dst.dst()):
    ny_market_open = 12
    nyse_market_open = 14
else:
    ny_market_open = 13
    nyse_market_open = 15

jp_market_open = 23

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

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

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

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

}

app.autodiscover_tasks()