celery是一个强大的分布式任务队列的异步处理框架,它可以让任务的执行完全脱离主程序,甚至可以被分配到其他主机上运行。通常用来实现实时处理的异步任务队列,和定时任务调度。
异步任务:将耗时任务提交给celery去异步执行,比如发送短信/邮件、消息推送、音视频处理等。
定时任务:定时执行某件事情,比如每天数据统计。
celery架构由三部分组成,消息中间件(message broker),任务执行单元(worker)和任务执行结果存储(result backend)组成。
producer:任务生产者,调用了celery提供的api、函数或者装饰器,而产生任务并交给任务队列处理的都是生产者。
broker:消息中间件,接受任务生产者发送过来的任务消息,存进任务队列并按顺序分发给任务消费方。celery本身不提供消息服务,但是可以方便的和第三方提供的消息中间件集成,包括RabbitMQ, Redis等。
worker:执行任务的消费者,通常在多台服务器上运行多个消费者来提高执行效率。
result backend:任务结果存储,用来存储Worker执行的任务结果,Celery支持以不同方式存储任务的结果,包括RabbitMQ, redis等。
celery beat:任务调度器,beat进程会周期性地将配置中到期需要执行的任务发送给任务队列。
pip install celery
pip install eventlet # 在windows跑任务时需要eventlet,通过协程实现并发
项目目录结构
flask_celery
│ get_async_result.py
│ produce_task.py
│─async_tasks
│ │ create_celery.py
│ │ task_email.py
创建celery对象
create_celery.py
from celery import Celery
celery_app = Celery("celery_demo",
broker="redis://127.0.0.1:6379/1",
backend="redis://127.0.0.1:6379/2",
include=["async_tasks.task_email", "async_tasks.task_msg"]
)
创建异步任务
task_email.py
import time
from async_tasks.create_celery import celery_app
@celery_app.task
def send_email(name):
print(f"start to send email to {name}")
time.sleep(5)
return f"send email to {name} ok !"
主流程调异步任务
produce_task.py
from async_tasks.task_email import send_email
result_email = send_email.delay("lisi")
print(f"send email task id is {result_email}")
获取异步任务结果
get_async_result.py
from celery.result import AsyncResult
from async_tasks.create_celery import celery_app
# 这块的task_id,不是produce_task.py的result_email,result_email是AsyncResult类,传参应该使用result_email.id属性
async_result = AsyncResult(task_id, app=celery_app)
if async_result.successful():
task_result = async_result.get()
print("task exec succeed")
print(task_result)
elif async_result.failed():
print("task exec failed")
elif async_result.status == "PENDING":
print("task exec is waiting")
elif async_result.status == "RETRY":
print("task exec is retrying after fail")
elif async_result.result == "STARTED":
print("task exec is ready to exec")
else:
print("others")
命令行开启worker
cd flask_celery # 先进入项目跟目录
celery -A async_tasks.create_celery worker -l info -P eventlet # 开启worker
注意:
如果不进入到项目跟目录,那么在实例化Celery的include参数要从项目根目录开始,命令行 -A 后面的参数也要从项目根目录开始,否则报错:没有xxx模块
项目目录结构
flask_celery
│ timing_task.py
│─async_tasks
│ │ create_celery.py
│ │ task_msg.py
增加定时任务
task_msg.py
import time
from async_tasks.create_celery import celery_app
@celery_app.task
def send_msg(id):
print(f"start to send msg to {id}")
time.sleep(5)
return f"send msg to {id} ok !"
创建celery对象,并制定定时策略
create_celery.py
from datetime import timedelta
from celery import Celery
from celery.schedules import crontab
celery_app = Celery("celery_demo",
broker="redis://127.0.0.1:6379/1",
backend="redis://127.0.0.1:6379/2",
include=["async_tasks.task_email", "async_tasks.task_msg"])
celery_app.conf.timezone = "Asia/Shanghai"
celery_app.conf.enable_utc = False
celery_app.conf.beat_schedule = {
# 定时任务名字
"add_timing_email": {
"task": "async_tasks.task_email.send_email", # 要执行的定时任务函数, 必须到函数名
# "schedule": 10, # 每10秒执行一次
# "schedule": crontab(minutes="*/10") # crontab表达式
"schedule": timedelta(seconds=10), # 间隔10秒
"args": ("zhangsan", )
},
# 定时任务名字
"add_timing_msg": {
"task": "async_tasks.task_msg.send_msg",
"schedule": crontab(minute="*/10"),
"args": ("lisi",)
}
}
也可以不制定定时策略,自己手写一个一次性执行的定时任务。
timing_task.py
from datetime import datetime
from async_tasks.task_msg import send_msg
t1 = datetime(2023, 5, 29, 11, 23, 00)
t2 = datetime.utcfromtimestamp(t1. timestamp())
result_msg = send_msg.apply_async(args=["zhangsan02"], eta=t2) # eta参数必须传入utc时间
print(f"send msg task id is {result_msg}")
命令行启动worker
cd flask_celery # 先进入项目跟目录
celery -A async_tasks.create_celery worker -l info -P eventlet # 开启worker
注意:
1. -A 后面的参数必须截至到生成celeryapp的文件名
2. 如果不进入到项目跟目录,那么在实例化Celery的include参数要从项目根目录开始,命令行 -A 后面的参数也要从项目根目录开始,否则报错:没有xxx模块
命令行启动定时任务
celery -A async_tasks.create_celery beat -l info
注意事项和异步任务同理。