当我将 Django Celery apply_async 与 eta 一起使用时,它会立即完成工作

Posted

技术标签:

【中文标题】当我将 Django Celery apply_async 与 eta 一起使用时,它会立即完成工作【英文标题】:When I use Django Celery apply_async with eta, it does the job immediately 【发布时间】:2017-12-18 10:18:59 【问题描述】:

我查看了 celery 文档并从中尝试了一些东西,但它不像示例那样工作。也许我在某些时候错了,如果我对以下代码有误,请给我一些指导

在views.py中我有这样的东西:

class Something(CreateView):
  model = something

  def form_valid(self, form):
    obj = form.save(commit=False)
    number = 5
    test_limit = datetime.now() + timedelta(minutes=5)
    testing_something.apply_async((obj, number), eta=test_limit)
    obj.save()

在芹菜任务中我写了这样的东西:

@shared_task()
def add_number(obj, number):
    base = Base.objects.get(id=1)
    base.add = base.number + number
    base.save()
return obj

我使用此代码的条件是 celery 在 CreateView 运行后立即运行,我的目标是在运行 Something CreateView 后 5 分钟内运行一次任务 add_number。非常感谢

编辑:

    我已尝试将eta 更改为countdown=180,但它仍会立即运行add_number 功能。我也尝试过更长的倒计时,但仍会立即运行 我试过@johnmoustafis 的回答,但还是一样,任务立即运行 我也试过@dana 的回答,但还是一样,任务立即运行

【问题讨论】:

【参考方案1】:

Celery 默认使用 UTC 时间。 如果您的时区“落后”于 UTC(UTC - HH:MM),datetime.now() 调用将返回一个“落后于”UTC 的时间戳,从而导致您的任务立即执行。

您可以改用datetime.utcnow()

test_limit = datetime.utcnow() + timedelta(minutes=5)

由于您使用的是 django,因此存在另一种选择:

如果您在setting.py 中设置了USE_TZ = True,则您已启用django timezone settings,您可以使用timezone.now() 代替datetime.utcnow()

from django.utils import timezone

...

test_limit = timezone.now() + timedelta(minutes=5)

【讨论】:

我已经尝试过 timezone.now() 和 datetime.utcnow() 即使我刚刚尝试使用 my_date = datetime.now(pytz.timezone('US/Pacific')) 但仍然是一样 @knightzoid 您是否在django.settings 中设置了适当的时区? docs.djangoproject.com/en/1.11/ref/settings/… 是的,我在settings.py中设置了时区TIME_ZONE = 'US/Pacific' 我已经使用pdb 在终端上进行了一些调试,所以我可以看到time_limit 的确切时间,但它对我来说显示了正确的时间 @knightzoid 尝试用countdown=time_in_seconds 替换eta,以检查这是否与预期的延迟有关,或者问题出在其他地方。还可以尝试设置CELERY_ALWAYS_EAGER=False 并使用timezone.now() 等重试。【参考方案2】:

'test_limit' 变量没有时区信息。所以 Celery 会将 eta 参数理解为 UTC 时间。

请使用修改后的代码:

class Something(CreateView):
    model = something

    def form_valid(self, form):
        obj = form.save(commit=False)
        number = 5

        test_limit = datetime.now()
        test_limit = test_limit.replace(tzinfo=tz.tzlocal())
        test_limit = test_limit + timedelta(minutes=5)

        testing_something.apply_async((obj, number), eta=test_limit)
        obj.save()

【讨论】:

【参考方案3】:

您可能有CELERY_ALWAYS_EAGER=True 设置。

您能否也发布您的配置和您正在使用的 Celery 版本?

Here你可能会找到一些有用的信息。

【讨论】:

我将CELERY_ALWAYS_EAGER 更改为False,但还是一样。我使用celery==3.1.19,这是我在settings.py 上的芹菜设置CELERY_IMPORTS = ("somewhere.utils.tasks", ) CELERYBEAT_SCHEDULE = 'send-post-office-mails': 'task': 'somewhere.utils.tasks.send_post_office_mails', 'schedule': crontab(), , 'rebuild-index': 'task': 'somewhere.utils.tasks.rebuild_search_index', 'schedule': crontab(minute=0, hour='*'), , CELERY_ALWAYS_EAGER = False 我在您的设置中看到的一些问题是您没有定义BROKER_URL,这可能是问题所在。关注this guide 可能会对您有所帮助。 我使用 redis 作为我的代理,我也把它放在我的 settings.py BROKER_URL = env("DJANGO_BROKER_URL", default='redis://localhost:6379/0')

以上是关于当我将 Django Celery apply_async 与 eta 一起使用时,它会立即完成工作的主要内容,如果未能解决你的问题,请参考以下文章

Django/Celery 和 CloudAMQP/Heroku 的连接错误

使用 Redis、Celery 设置 Django 以通过 Gmail 发送电子邮件

Django celery 4 - ValueError: int() 的无效文字,当启动 celery worker 时,基数为 10

为啥当我尝试在 celery 任务中使用模型时,django 会引发“应用程序尚未加载”错误?

django-celery-beat 垃圾邮件到期任务

celery beat 没有发送消息(使用 django-celery-beat)