Celery定时任务/延时任务
前面已经学习了Celery实现异步任务的提交,但是Celery也可以实现定时的任务,对于后端项目中,我们往往需要定时的清除/更新/备份一些相关内容,就可以通过celery进行执行。
1. 定时任务
Celery中自带有的apply_async方法,可以通过传入eta参数,实现定时任务的执行。
from celery_demo.sms_tasks import send_sms
# 导入已经使用装饰器装饰过的事件函数
from datetime import datetime
# 导入datetime日期时间对象
time_loc = datetime(2023,8,15,17,59,59)
# 定义datetime对象,设置时间为2023/8/15 17:59:59
# 这里的时间是计算机本地时间,也就是北京时间
# celery需要转换utc时间
time_utc = datetime.utcfromtimestamp(time_loc.timestamp())
# 转换utc时间对象
task = send_sms.apply_async(args=('小李',), eta=time_utc)
# apply_async 方法传入eta参数定时执行任务
print(task.id)
# 打印事件id,用于后续获取执行结果
apply_async方法如果不带eta参数,那其实和delay方法是一样的,仅仅就多了一个eta参数
2. 延时任务
Celery并没有直接设置的延时任务
需要我们自定义一个datetime对象进行延时任务的设置
from celery_demo.sms_tasks import send_sms
from datetime import datetime,timedelta
# timedelta模块用于设置时间差
now_time = datetime.utcnow()
# 获取当前的utc时间对象
timing = now_time + timedelta(seconds=10)
# 延时10分钟后,其他参数days/seconds/microseconds/...可以设置天/秒/微秒延时
task = send_sms.apply_async(args=('小李',), eta=now_time)
# 传入时间对象
print(task.id)
# 打印事件id,用于后续获取执行结果
3. 周期性定时任务
celery封装了beat_schedule,周期性任务,我们可以通过定义celery.conf.beat_schedule配置,进行周期性任务的设置,配置完成后,通过命令启动beat 进程,即可进行周期性定时任务。
3.1 周期性定时任务定义
from datetime import timedelta
from celery.schedules import crontab # 导入crontab表达式函数
from celery import Celery
cel = Celery('CeleryDemo',
broker='redis://192.168.32.128:6379/0',
backend='redis://192.168.32.128:6379/1',
include=[
'celery_demo.sms_tasks',
'celery_demo.wechat_tasks'
])
cel.conf.beat_schedule = {
# 这里的名字可以随便取,是对定时任务的命名
# 但是里面的参数,不可以更改!
"xxx名字随便定义": {
"task": "celery_demo.sms_tasks.send_sms",
"schedule": timedelta(seconds=6), # 定时6秒执行一次
# "schedule": 10.0, # 每10秒执行一次
# "schedule": crontab(minute='*/1'), # 定时每分钟执行一次
# 使用的是crontab表达式
"args": ('李四',) # 函数传参的值
}
}
3.2 使用命令启动beat任务
celery -A myapp beat --loglevel=info
此时,我们可以看到,任务已经在以6秒一次去循环提交
3.3 crontab表达式
crontab表达式是非常成熟但是复杂的表达式,它可以控制实现周期性任务,例如每周/每天/几点/偶数时间/周几…等各类复杂时间表达式定时。
3.3.1 crontab参数详解
crontab 函数的所有参数及其解释如下:
minute
:分钟,取值范围为0-59或者*
表示匹配所有分钟。hour
:小时,取值范围为0-23或者*
表示匹配所有小时。day_of_week
:星期几,取值范围为0-6(0表示周一,6表示周日)或者*
表示匹配所有星期几。day_of_month
:月份中的日期,取值范围为1-31或者*
表示匹配所有日期。month_of_year
:年份中的月份,取值范围为1-12或者*
表示匹配所有月份。timezone
:时区,可以使用pytz.timezone
函数来指定时区,默认为None
表示使用UTC时区。
3.3.2 使用案例
- 每分钟执行一次:
schedule = crontab(minute='*/1')
- 每小时的第30分钟执行一次:
schedule = crontab(minute=30)
- 每天的午夜执行一次:
schedule = crontab(minute=0, hour=0)
- 每周一的上午10点执行一次:
schedule = crontab(minute=0, hour=10, day_of_week=1)
- 每个月的第一天午夜执行一次:
schedule = crontab(minute=0, hour=0, day_of_month=1)
- 每隔5分钟执行一次,但只在周一和周二执行:
schedule = crontab(minute='*/5', day_of_week='mon,tue')
- 每小时执行一次,但排除周末(周六和周日):
schedule = crontab(minute=0, hour='*/1', day_of_week='mon-fri')
- 每天的上午9点至下午5点,每隔30分钟执行一次:
schedule = crontab(minute='*/30', hour='9-17')
- 每月的1号和15号,上午10点和下午4点执行一次:
schedule = crontab(minute=0, hour='10,16', day_of_month='1,15')
3.3.3 更改时区
如果需要按照本地中国时间,进行定时任务的执行,则需要使用timezone,默认使用的是utc时间
from celery.schedules import crontab
from pytz import timezone # 导入timezone
crontab(hour=22, minute=0, tz=timezone('Asia/Shanghai'))
# 定义上海(=北京)时间,每天晚上10点整执行。
4. 周期性任务踩坑
首先,周期性任务,是一个开启后就会不断提交的任务。
这里我们要看清楚最重要的两个字:提交
也就是说,周期性任务beat开启后,并不是直接执行,而是重复的提交到队列中
我们现在使用
celery -A myapp beat --loglevel=info
将周期性提交任务启动,此时只是任务被提交到队列,但是并不会执行,因为并没有开启worker
通过Redis,我们可以看到celery-list中的数据,在不断的增加,此时会大量的积压任务,当我们开启beat进程后,就会瞬间释放任务,将其全部执行。
此时Redis中队列长度变为0,已经全部执行结束
4.1 清除所有积压任务
但是,在实际的生产过程中,对于已经积压的任务,我们往往都是希望去删除掉,而不是给他全部都执行了,如果希望在执行新的beat任务之前,清除所有的积压任务,可以使用 purge 参数,清除所有队列任务
celery -A celery实例名/包 purge
eg:
celery -A celery_demo.main purge # 清除celery_demo.main中实例的所有积压任务
可以发现,已经清除完成!
,清除所有的积压任务,可以使用 purge 参数,清除所有队列任务**
celery -A celery实例名/包 purge
eg:
celery -A celery_demo.main purge # 清除celery_demo.main中实例的所有积压任务
可以发现,已经清除完成!