celery 简介
一. 介绍
-
celery:翻译过来叫---> "芹菜", 它是一个 分布式的异步任务 框架
-
celery是独立的服务
( 1 ) 可以不依赖任何服务器,通过自身命令,启动服务
( 2 ) celery服务为其他项目服务提供异步解决任务需求的
注:会有两个服务同时运行,一个是项目服务,一个是celery服务,项目服务将需要异步处理的任务交给celery服务,celery就会在需要时异步完成项目的需求
- celery 的作用
( 1 ) 完成异步任务:可以提高项目的并发量,之前开启线程做,现在使用celery做
( 2 ) 完成延迟任务
( 3 ) 完成定时任务 - 架构
( 1 ) 消息中间件:broker 提交的任务(函数)都放在这里,celery本身不提供消息中间件,需要借助于第三方:redis,rabbitmq
( 2 ) 任务执行单元:worker,真正执行任务的地方,一个个进程,执行函数。
( 3 ) 结果存储:backend,函数return的结果存储在这里,celery本身不提供结果存储,借助于第三方:redis,数据库,rabbitmq。
二. 使用(简单小案例)
celery 路径导入是个大坑,切记切记。
1. 安装模块
# 安装模块
pip install celery
# 安装redis (需要用到)
pip install redis
# 安装 eventlet (windows 系统需要用到,因为 celery 不支持 windows)
pip install eventlet
2. 代码
(1). celery_task.py
"""------------------------初始化工厂-------------------------"""
# 导入
from celery import Celery
import time
# 存储结果的地方 (可以不用 redis)
broker = 'redis://127.0.0.1:6379/1'
# 提交任务的地方 (可以不用 redis)
backend = 'redis://127.0.0.1:6379/2'
app = Celery('app', broker=broker, backend=backend)
# 加上装饰器, 才算是celery的任务。
@app.task
def add(a, b): # 写任务,任务就是函数。
time.sleep(2)
print(a + b)
return a + b
"""------------------------启动工厂-------------------------"""
#"""需要进入 celery_task.py 所在的文件下执行"""
# 启动后等待任务提交,如果有任务提交,会自动执行。
# windos 系统运行:《 celery_task,就是文件名 》
celery -A celery_task worker -l info -P eventlet
# 非 windos 运行:mac linux
celery -A celery_task worker -l info
(2). submit_task.py, 提交任务
《1》. add.delay(3, 4) 函数名加括已经调用了,非项目中,右键执行即可。
《2》. 在项目中,把函数放在需要执行的位置进行调用,项目执行时,就会被调用。
《3》. 在项目中,用异步任务。
from celery_task import add
# 同步任务,直接就执行了。
res = add(3, 4)
print(res) # 打印结果:7
# 异步任务了,不会将代码结果执行出来, 而是放在了 borker 层中, 等待 worker 层取任务执行。
# 结果是任务 id 号,celery 会根据 id 号执行任务,最后返回结果。
res = add.delay(3, 4)
print(res) # 打印结果:8b392207-f69d-4544-8d96-953bb8453be0 (任务 id 号)
(3). check_task.py, 查看任务执行结果
# 导入任务
from celery_task import app
from celery.result import AsyncResult
# (2)中提交后得到的任务 id
id = "12313"
if __name__ == '__main__':
res = AsyncResult(id=id, app=app)
if res.successful():
result = res.get()
print(result)
elif res.failed():
print("任务失败")
elif res.status == 'PENDING':
print("任务等待中执行")
elif res.status == 'RETRY':
print("任务异常后正在重试")
elif res.status == 'STARTED':
print("任务已经开始执行")
三. 包结构(项目级)
celery 路径导入是个大坑,切记切记。
1. 目录结构
# 具体可见下面图片
项目:
# 新建包名
|—— celery_task
|—— __init__.py
# 初始化 app
|—— init_celery.py
# 任务1
|—— add_task
|—— __init__
|—— add_task.py # 编写任务的文件
# 任务2
|—— send_task
|—— __init__
|—— send_task.py # 编写任务的文件
"""下面提交任务和获取结果,在项目中,就需要在业务代码中进行了。"""
# 提交任务1
|—— summit_task_01.py
# 提交任务2
|—— summit_task_02.py
# 获取任务执行的结果
|—— get_result.py
2. 代码
启动命令
# 启动命令
celery -A celery_task.init_celery worker -l info -P eventlet
# 如果(1)中的文件名是 celery.py 命令可以如下:
celery -A celery_task worker -l info -P eventlet
(1). init_celery.py (初始化)
写完初始化
代码,就可以启动 celery。启动命令如上面图片所示:
图片灰色背景
部分就是初始化
文件的路径
from celery import Celery
broker = 'redis://127.0.0.1:6379/1'
# 提交任务的地方 (可以不用 redis)
backend = 'redis://127.0.0.1:6379/2'
# 1. 因为任务代码放到了其他文件中,需要 include 指定位置。
# 路径为:任务函数所在的文件名为止
app = Celery('app', broker=broker, backend=backend, include=['celery_task.add_task.add_task', 'celery_task.send_task.send_task'])
(2). add_task.py(任务)
from celery_task.init_celery import app
@app.task()
def add(a, b):
return a + b
(3). summit_add_task.py (提交)
from celery_task.add_task.add_task import add
# 脚本形式运行 《项目里在业务代码中》
if __name__ == '__main__':
res = add.delay(1, 1)
print(res)
(4). get_result.py (取结果)
from celery_task import app
from celery.result import AsyncResult
#(3)中提交后得到的任务 id
id = "12313"
# 脚本形式运行 《项目里在业务代码中》
if __name__ == '__main__':
res = AsyncResult(id=id, app=app)
if res.successful():
result = res.get()
print(result)
elif res.failed():
print("任务失败")
elif res.status == 'PENDING':
print("任务等待中执行")
elif res.status == 'RETRY':
print("任务异常后正在重试")
elif res.status == 'STARTED':
print("任务已经开始执行")
四:官方推荐(结构)
1. 项目结构
|—— 大项目名
|—— 小项目名
|—— __init__.py
|—— settings
|—— __init__.py
|—— dev.py
|—— celery01.py
|—— app01
...
|—— task.py
|—— views.py
1. 创建 celery
在《小项目》下创建 celery 文件, 本项目以 celery01.py 为例
官方推荐 文件名为 celery.py, 但是有导包问题。
celery.py
文件中的代码如下:
import os
import django
from celery import Celery
# 导入配置文件
os.environ.setdefault('DJANGO_SETTINGS_MODULE', '<配置文件路径> eg:luffy_api.settings.dev')
# 实例化 celery 对象
app = Celery('celery_demo')
方案一:代码在下方
# 方案二:
# 自动检索配置文件中获取相关 celery 配置
app.config_from_object('django.conf:settings')
# 自动检索 app 下的task.py文件
app.autodiscover_tasks()
# app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)
方案一代码:
app.conf.update(
BROKER_URL='redis://127.0.0.1:6379/1',
# BACKEND配置,使用redis
RESULT_BACKEND='redis://127.0.0.1:6379/2',
ACCEPT_CONTENT=['json'],
TASK_SERIALIZER='json',
# 结果序列化方案
RESULT_SERIALIZER='json',
# 任务结果过期时间,秒
TASK_RESULT_EXPIRES=60 * 60 * 24,
# 时区配置
TIMEZONE='Asia/Shanghai',
)
方案二代码:
可以放在任意配置文件
中,被调用了就行。
BROKER_URL='redis://127.0.0.1:6379/1',
# BACKEND配置,使用redis
RESULT_BACKEND='redis://127.0.0.1:6379/2',
ACCEPT_CONTENT=['json'],
TASK_SERIALIZER='json',
# 结果序列化方案
RESULT_SERIALIZER='json',
# 任务结果过期时间,秒
TASK_RESULT_EXPIRES=60 * 60 * 24,
# 时区配置
TIMEZONE='Asia/Shanghai',
2. 引入 celery-app
在小项目下的 __init__.py 文件内容如下:
from .celery01 import app as celery_app
__all__ = ('celery_app', )
3. 创建 task
在每一个 app 下创建 task.py 文件(命名必须是 task/tasks), 写 celery 任务
from celery import shared_task
# 使用装饰器装饰后, 就是 celery 任务函数了。
@shared_task()
def add1(a, b):
return a + b
4. 调用任务
在需要使用的地方调用,比如 views.py
# 引入 task.py 中的任务函数
from task import add1
# 使用
add1.delay(a, b)
5. 启动任务
# celery -A <celery的路径名> worker -l debug -P eventlet
celery -A luffy_api.celery01 worker -l debug -P eventlet
五,异步,延迟,定时任务
1. 异步任务
# 导入任务
from celery_task.add_task.add_task import add
# 提交任务
res = add.delay(7, 6)
# 打印任务对应的 id
print(res)
2. 延迟任务
# 导入任务
from celery_task.add_task.add_task import add
# 导入时间模块
from datetime import datetime, timedelta
# 对应的是 utc 时间 《我们是东八区,可以通过配置改, 配置可见 3.定时任务 中的代码》
# datetime.utcnow(), 当前 utc 时间
# timedelta(seconds=10), 10 秒后
"""
如果没有延时执行,一定要把下面这段代码放到函数里,使其能够再次执行即可。
"""
eta = datetime.utcnow() + timedelta(seconds=10)
# 提交任务
# 参数:args: 对应任务函数中的形参。 eta: 需要对应时间对象, 不能只写一个数字
res = add.apply_async(args=(11, 12), eta=eta)
# 打印任务对应的 id
print(res)
3. 定时任务
1. 坑 坑 坑 :使用官方推荐结构时。
- 使用
定时任务
,task 中的任务路径可能找不到,需将task.py
改为tasks.py
- 配置
""" -------------------------------- celery.py 中配置 ----------------------------------- """
# 导入模块
from datetime import timedelta
# 时区, 所有配置字典,默认的
app.conf.timezone = 'Asia/Shanghai'
# 不使用 utc 时间
app.conf.enable_utc = False
# 配置
app.conf.beat_schedule = {
# 第一个任务
'send_sms_task': {
# 任务对应的路径---》路径不要写错
'task': 'celery_task.task_01.add',
# 定时任务间隔的时间
# 'schedule': crontab(hour=8, day_of_week=1), # 每周一早八点
# 每隔5秒
'schedule': timedelta(seconds=5),
# 对应的参数
'args': (15, 25),
},
# 第二个任务 (同上)
'add_task': {
'task': 'celery_task.task_02.add2',
# 每周三早十二点
'schedule': crontab(hour=12, minute=10, day_of_week=3),
# 对应的参数
'args': (10, 20),
}
}
- 启动(两者顺序可以调换)
# 启动 beat :提交任务的人
celery -A celery_task beat -l info
# 启动 worker :干活的人
celery -A celery_task worker -l info -P eventlet
六. Celery 监控工具
1. 使用Flower的Webhook功能
Flower
支持 Webhook
,可以在特定事件发生时(如任务失败)发送HTTP请求到你指定的URL。你可以设置一个接收这些请求的服务,并在接收到请求后发送告警。
# 下载
pip install flower
# 在Flower的配置文件中(或通过命令行参数)配置Webhook:
celery flower --broker=redis://localhost:6379/0 --conf=flowerconfig.py
在 flowerconfig.py
中配置 Webhook
webhook = {
'url': 'http://your-webhook-receiver/endpoint',
'events': ['task-failed']
}
2. 使用Celery的错误处理回调
你可以在任务中定义错误处理回调,当任务失败时,回调函数会被调用,你可以在回调函数中发送告警。
from celery import Celery
app = Celery('myapp', broker='redis://localhost:6379/0')
@app.task(bind=True)
def my_task(self):
try:
# 任务逻辑
pass
except Exception as exc:
# 发送告警
send_alert(f"Task {self.name} failed: {exc}")
raise
def send_alert(message):
# 发送告警的逻辑,例如发送电子邮件、短信等
pass