本篇内容主要在讨论,该如何让 celery 在指定时间执行任务
过程中如果有错误,欢迎留言讨论喔 ~
一、建立 task
from celery import Celeryfrom setting import broker_url, backend_urlfrom datetime import datetimeapp = Celery("celery_start", broker=broker_url, backend=backend_url)@app.taskdef get_time(name: str): now = datetime.now() return f"Hello {name} 现在时间为 {now.strftime('%Y-%m-%d, %H:%M:%S')}"
二、countdown 参数
countdown 参数单位为秒,使用这个参数可以让 celery worker 在收到任务后
过了指定单位的秒数再执行
get_time.apply_async(("nick",), countdown=20)
下图中我们可以看到 celery 在收到任务后,过了大约 20 秒才执行任务
三、eta 参数
eta 参数接收的资料型态须为 datetime 物件,但要特别注意,celery 内部所接收的时间
是 UTC 标準时间,虽然说 celery 本身有提供一些 config 可以让使用者作设定,但是对
于时区的部分似乎是没有效果,因此如果要利用 eta 的话我们必须使用 pytz 这个套件对
时间做转换,下方为 pytz 的範例
from celery_schedule import get_timefrom datetime import datetime, timedeltaimport pytz# 先设定时区local_timezone = pytz.timezone("Asia/Taipei")# 产生一笔时间物件,并计算想要甚么时候执行任务# 也可以直接利用 datetime 直接指定一个日期now = datetime.now()exec_time = now + timedelta(seconds=10)# 利用 pytz 进行转换exec_time = local_timezone.localize(exec_time)# 送出任务get_time.apply_async(("nick",), eta=exec_time)
如下图所示,可以看到大约在 10 秒钟后任务就会被 celry worker 执行
四、expries 参数
expries 参数可以理解程,任务的到期时间,也就是这个任务最晚甚么时候要被执行,
前面有提到,celery 可以自动将任务进行排队,由于 worker 的 process 的数量有限
如果一次送太多任务,任务就必须进行排队,因此可以透过设定这个参数来告诉 celery
,该任务最晚必须于甚么时候执行
附注: 若 celery 收到 expries 已经过期的任务时,则 celery 会将任务状态标记为 revoke,代表不会去执行这个任务,任务状态相关的部分会于下一篇文章进行解说
附注: expries 可以接收的资料型态为 int (用于指定秒数) 以及 datetime (用于指定日期)
from celery_schedule import get_timefrom datetime import datetime, timedeltaimport pytz# 先设定时区local_timezone = pytz.timezone("Asia/Taipei")# 产生一笔时间物件,并计算想要甚么时候执行任务# 也可以直接利用 datetime 直接指定一个日期now = datetime.now()exec_time = now + timedelta(seconds=10)# 利用 pytz 进行转换exec_time = local_timezone.localize(exec_time)# 送出任务get_time.apply_async(("nick",), expries=exec_time)
如下图所示,可以看到 celery 在收到任务后马上执行,因为版主只有送了一个任务进入列队
五、redis 重複派送任务问题
redis 对于长时间 eta 的处理不太优,常常会产生非预期的问题,例如重複派送任务等,可以看看这篇文章
若遇上此问题,可以考虑将 broker 换成 rabbitmq 或是利用 celery beat 重複检查看看是否有任务到期
若到期再用 delay 送进任务列队即可