Celery 并发异步框架
1.什么是Celery
Celery是一个简单、灵活且可靠的,处理大量消息的分布式系统、专注于实时处理的异步任务队列、同时也支持任务调度。
2.消息中间件
Celery本身不提供消息服务,但是可以方便的和第三方提供的消息中间件集成。包括,RabbitMQ, Redis等等
3.任务执行单元
Worker是Celery提供的任务执行的单元,worker并发的运行在分布式的系统节点中。
4.任务结果存储
Task result store用来存储Worker执行的任务的结果,Celery支持以不同方式存储任务的结果,包括AMQP, redis等
5.Celery使用场景
异步任务:将耗时操作任务提交给Celery去异步执行,比如发送短信/邮件、消息推送、音视频处理等等
6.Celery的安装配置
pip install celery
消息中间件:RabbitMQ/Redis
app=Celery('任务名',backend='xxx',broker='xxx')
7.Celery执行异步任务
基本使用
celery_app_task.py
import celery
# 消息接收库
broker = 'redis://172.17.0.66:6379/2'
# 消息存储库
backend = 'redis://172.17.0.66:6379/1'
cel = celery.Celery('test', broker=broker, backend=backend)
@cel.task
def add(x, y):
return x + y
@cel.task
def send_email(name):
print("向%s发送邮件..." % name)
time.sleep(1)
print("向%s发送邮件完成" % name)
return "ok"
@cel.task
def send_msg(name):
print("向%s发送短信..." % name)
time.sleep(1)
print("向%s发送短信完成" % name)
return "ok"
add_task.py,添加任务
from celery_app_task import send_email, send_msg,add
result = add.delay(4,4)
print(result.id)
result = send_email.delay("他")
print(result.id)
result = send_msg.delay("我")
print(result.id)
启动worker
celery -A celery_app_task worker -l info -P eventlet
result.py,查看任务执行结果
from celery.result import AsyncResult
from celery_app_task import cel
async_result = AsyncResult(id="d6dbc2d4-956a-46ed-93dc-71ca8a18a67e", app=cel)
if async_result.successful():
result = async_result.get()
print(result)
# result.forget() # 将结果删除
elif async_result.failed():
print('执行失败')
elif async_result.status == 'PENDING':
print('任务等待中被执行')
elif async_result.status == 'RETRY':
print('任务异常后正在重试')
elif async_result.status == 'STARTED':
print('任务已经开始被执行')
8.Celery多目录异步执行
多任务结构
celery_tasklist
├── celery_task # celery相关文件夹
│ ├── celery.py # celery连接和配置相关文件,必须叫这个名字
│ └── task01.py # 所有任务函数
│ └── task02.py # 所有任务函数
├── check_result.py # 检查结果
└── send_task.py # 触发任务
celery.py
from celery import Celery
cel = Celery('celery_demo',
broker='redis://172.17.0.66:6379/1',
backend='redis://172.17.0.66:6379/2',
# 包含以下两个任务文件,去相应的py文件中找任务,对多个任务做分类
include=['celery_task.task01',
'celery_task.task02'
])
# 时区
cel.conf.timezone = 'Asia/Shanghai'
# 是否使用UTC
cel.conf.enable_utc = False
task01.py
import time
from celery_task.celery import cel
@cel.task
def send_email(name):
print("向%s发送邮件..." % name)
time.sleep(5)
return "邮件完成!!!"
task02.py
import time
from celery_task.celery import cel
@cel.task
def send_msg(name):
print("向%s发送短信..." % name)
time.sleep(5)
return "短信完成!!!"
send_task.py
from celery_task.task01 import send_email
from celery_task.task02 import send_msg
# 立即告知celery去执行send_email任务,并传入一个参数
result = send_email.delay('第一个的执行')
print(result.id)
result = send_msg.delay('第二个的执行')
print(result.id)
check_result.py
from celery.result import AsyncResult
from celery_task.celery import cel
async_result = AsyncResult(id="ad409177-a164-4dd7-9eca-0be5018b9fce", app=cel)
if async_result.successful():
result = async_result.get()
print(result)
# result.forget() # 将结果删除,执行完成,结果不会自动删除
# async.revoke(terminate=True) # 无论现在是什么时候,都要终止
# async.revoke(terminate=False) # 如果任务还没有开始执行呢,那么就可以终止。
elif async_result.failed():
print('执行失败')
elif async_result.status == 'PENDING':
print('任务等待中被执行')
elif async_result.status == 'RETRY':
print('任务异常后正在重试')
elif async_result.status == 'STARTED':
print('任务已经开始被执行')
添加任务(执行send_task.py),开启work:celery -A celery_task worker -l info -P eventlet ,检查任务执行结果(执行check_result.py)
9.Celery执行定时任务
基本使用
celery_tasks.py
import celery
import time
broker = 'redis://172.17.0.66:6379/2'
backend = 'redis://172.17.0.66:6379/1'
cel = celery.Celery('test', broker=broker, backend=backend)
@cel.task
def send_email(name):
print("向%s发送邮件..." % name)
time.sleep(1)
print("向%s发送邮件完成" % name)
return "ok"
@cel.task
def send_msg(name):
print("向%s发送短信..." % name)
time.sleep(1)
print("向%s发送短信完成" % name)
return "ok"
produce_task.py
from celery_tasks import send_email
from datetime import datetime, timedelta
# 方式一(定义哪个时间执行)
v1 = datetime(2022, 2, 18, 17, 26, 56)
print(v1)
v2 = datetime.utcfromtimestamp(v1.timestamp())
print(v2)
# args参数是传参
result = send_email.apply_async(args=["mzk"], eta=v2)
print(result.id)
# 方式二(延时多久执行)
ctime = datetime.now()
# 默认用utc时间
utc_ctime = datetime.utcfromtimestamp(ctime.timestamp())
time_delay = timedelta(seconds=10) # seconds设置多少秒
task_time = utc_ctime + time_delay
# 使用apply_async并设定时间
result = send_email.apply_async(args=["mzk"], eta=task_time)
print(result.id)
check_result.py
from celery.result import AsyncResult
from celery_tasks import cel
async_result = AsyncResult(id="2b35127e-bd74-42f9-9424-2842262f1549", app=cel)
if async_result.successful():
result = async_result.get()
print(result)
# result.forget() # 将结果删除,执行完成,结果不会自动删除
# async.revoke(terminate=True) # 无论现在是什么时候,都要终止
# async.revoke(terminate=False) # 如果任务还没有开始执行呢,那么就可以终止。
elif async_result.failed():
print('执行失败')
elif async_result.status == 'PENDING':
print('任务等待中被执行')
elif async_result.status == 'RETRY':
print('任务异常后正在重试')
elif async_result.status == 'STARTED':
print('任务已经开始被执行')
添加任务(执行celery_tasks.py),开启work:celery -A celery_task worker -l info -P eventlet ,检查任务执行结果(执行check_result.py)
10.Celery多目录下定时任务
多任务结构中celery.py修改如下
from celery import Celery
from celery.schedules import crontab
from datetime import timedelta
cel = Celery('celery_demo',
broker='redis://172.17.0.66:6379/1',
backend='redis://172.17.0.66:6379/2',
# 包含以下两个任务文件,去相应的py文件中找任务,对多个任务做分类
include=['celery_task.task01',
'celery_task.task02'
])
# 时区
cel.conf.timezone = 'Asia/Shanghai'
# 是否使用UTC
cel.conf.enable_utc = False
cel.conf.beat_schedule = {
# 名字随意命名
'add-every-10-seconds': {
# 执行task01下的send_email函数
'task': 'celery_task.task01.send_email',
# 每隔5秒执行一次
# 'schedule': 1.0,
# 'schedule': crontab(minute="*/1"),
'schedule': timedelta(seconds=5),
# 传递参数
'args': ('mzk',)
},
# 'add-every-12-seconds': {
# 'task': 'celery_task.tasks1.test_celery',
# 每年4月11号,8点42分执行
# 'schedule': crontab(minute=42, hour=8, day_of_month=11, month_of_year=4),
# 'schedule': crontab(minute=42, hour=8, day_of_month=11, month_of_year=4),
# 'args': (16, 16)
# },
}
启动work执行:celery -A celery_task worker -l info -P eventlet # windows下使用eventlet多线程
启动一个beat:celery -A celery_task beat -l info # Celery Beat进程会读取配置文件的内容,周期性的将配置中到期需要执行的任务发送给任务队列
11.django中使用Celery
项目根目录创建celery包,目录结构如下
celery_tasklist
├── email
│ ├── __init__.py
│ └── tasks.py # 任务函数
├── message
│ ├── __init__.py
│ └── tasks.py # 任务函数
├── init__.py
├── config.py # redis或rabbitmq连接url和存储url
└── main.py # 加载django配置任务
email文件下tasks任务
# celery的任务必须写在tasks.py的文件中,别的文件名称不识别!!!
import logging
log = logging.getLogger("django")
@app.task # name表示设置任务的名称,如果不填写,则默认使用函数名做为任务名
def send_email(name):
print("向邮箱%s发送邮件成功!" % name)
time.sleep(5)
return "send_email OK"
message文件下tasks任务
# celery的任务必须写在tasks.py的文件中,别的文件名称不识别!!!
from djcelery.main import app
import time
import logging
log = logging.getLogger("django")
@app.task # name表示设置任务的名称,如果不填写,则默认使用函数名做为任务名
def send_message(mobile):
"""发送短信"""
print("向手机号%s发送短信成功!" % mobile)
time.sleep(5)
return "send_message OK"
config.py
broker_url = 'redis://172.17.0.66:6379/15' # redis 中间件
result_backend = 'redis://172.17.0.66:6379/14' # redis 存储库
main.py 主程序中对django的配置文件进行加载
import os
from celery import Celery
import djangoProject2
# 创建celery实例对象
app = Celery("djcelery")
# 把celery和django进行组合,识别和加载django的配置文件 djangoProject2是settings上的文件夹名称
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'djangoProject2.settings')
# 通过app对象加载配置
app.config_from_object("djcelery.config")
# 加载任务
# 参数必须必须是一个列表,里面的每一个任务都是任务的路径名称
# app.autodiscover_tasks(["任务1","任务2"])
app.autodiscover_tasks(["djcelery.email", "djcelery.message"])
启动Celery的命令
强烈建议切换目录到mycelery根目录下启动
celery -A djcelery.main worker -l info -P eventlet
views.py视图调用
from django.shortcuts import HttpResponse
from djcelery.email.tasks import send_email
from djcelery.message.tasks import send_message
from datetime import timedelta
from datetime import datetime
def user(request):
# 异步任务
# 1. 声明一个和celery一模一样的任务函数,但是我们可以导包来解决
# send_email.delay("110")
# send_message.delay("119")
# send_sms.delay() 如果调用的任务函数没有参数,则不需要填写任何内容
# 定时任务
ctime = datetime.now()
# 默认用utc时间
utc_ctime = datetime.utcfromtimestamp(ctime.timestamp())
time_delay = timedelta(seconds=10)
task_time = utc_ctime + time_delay
result = send_message.apply_async(["911", ], eta=task_time)
# print(result.id)
return HttpResponse('OK')