当我将 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