Celery是python开发中广为使用的分布式任务队列框架,其整体框架如下图,包括消息中间件(Broker)、任务执行单元Worker、结果存储(Backend)三大部分。
本文仅举Celery在异步任务和定时任务的两个小示例。Backend采用redis-4.0.11, Celery版本为4.1.1。读者请注意不同版本redis和Celery的兼容性,同时注意不同版本Celery的API间的差异。
一、异步任务
步骤一:在task.py文件中编写异步任务代码
from celery import Celery
import time
broker_url = "redis://127.0.0.1:6379/1" # 使用redis存储任务队列
result_backend = "redis://127.0.0.1:6379/2" # 使用redis存储结果
app = Celery("asyn_demo", broker=broker_url, backend=result_backend) # 实例化 Celery对象
app.autodiscover_tasks('task01')
@app.task(name='task01')
def task01(string):
"""
异步代码
"""
time.sleep(10)
with open('1.txt', 'a+', encoding='utf-8') as f:
f.write("异步测试{}\n".format(string))
def task_run():
task01.apply_async(args=["第一次尝试"]) # apply_async异步操作
task01.apply_async(args=["第二次尝试"])
if __name__ == '__main__':
task_run()
步骤二:终端cd到task.py文件所在目录,并启动worker
celery -A task.app worker --loglevel=info
其中,需要注意的参数的意义如下:
步骤三:启动task.py程序,即可观察到异步任务的执行。
二、定时任务
整个Celery定时任务脚本的文件层级架构(当然,异步任务也可以采用该架构)如下:
步骤一:配置celery文件,并编写定时任务
celery_config.py 配置文件
# -*- coding: utf-8 -*-
# @Time : 2019/11/29 下午1:56
# @Author : guofei
# @File : celery_config.py
from celery.schedules import crontab, timedelta
broker_url = "redis://127.0.0.1:6379/2" # 使用redis存储任务队列
result_backend = "redis://127.0.0.1:6379/6" # 使用redis存储结果
timezone = "Asia/Shanghai" # 定义时区
imports = [
"celery_task.epp_scripts.test1", # 导入定时任务的py文件
"celery_task.epp_scripts.test2",
]
# 需要执行任务的配置,通过beat__schedule来实现
beat_schedule = {
"test1": {
"task": "celery_task.epp_scripts.test1.celery_run", #定时任务函数
"schedule": timedelta(seconds=30), # 调度时间
"args": () # 任务函数参数,元组
},
"test2": {
"task": "celery_task.epp_scripts.test2.celery_run",
"schedule": timedelta(seconds=20),
"args": ()
},
}
celery_main.py 主函数
from celery import Celery
# 创建celery应用对象
app = Celery("auto_handler")
# 导入celery的配置信息
app.config_from_object("celery_task.celery_config")
test1.py 定时任务1函数
from ..celery_main import app
def test11():
print("test11----------------")
def test22():
with open('task02.txt', 'a+', encoding='utf-8') as f:
f.write('test22')
test11()
@app.task
def celery_run():
test11()
test22()
if __name__ == '__main__':
celery_run()
test2.py 定时任务2函数
from ..celery_main import app
def test33():
print("test3----------------")
def test44():
with open('task02.txt', 'a+', encoding='utf-8') as f:
f.write('test44')
test33()
@app.task
def celery_run():
test33()
test44()
if __name__ == '__main__':
celery_run()
步骤二: 终端cd到celery_task文件所在目录,并启动定时服务
celery -A celery_task.celery_main beat
步骤三: 终端cd到celery_task文件所在目录,并启动worker
celery -A celery_task.celery_main worker --loglevel=info
此时,就可以看到相关后台定时任务的执行。步骤二和步骤三可以合并到如下一条命令:
celery -B -A celery_task.celery_main worker --loglevel=info
三、其它事项
3.1 定时任务crontab
频率性的任务可以用timedelta函数进行时间间隔空值。若想在每天的每个固定时间点进行定时任务,则需要改用crontab,如:
"schedule": crontab(hour=18, minute=0) # 每天晚6点
此时,要特别注意celery_config.py 配置文件中需配置好时区timezone。
3.2 Celery-4.1.1版本中的bug
在该版本的Celery中,由于在读取实时时间now时的bug,无法对crontab做出正确的定时任务启动。此时,需要对celery_main.py 主函数中Celery对象进行如下修改:
class MyCelery(Celery):
def now(self):
"""Return the current time and date as a datetime."""
from datetime import datetime
return datetime.now(self.timezone)
app = MyCelery("my_handler")