Celery
Celery
1.Celery 简介
1.1celery 是什么
Celery(芹菜)是一个简单、灵活且可靠的,处理大量消息的分布式系统,并且提供维护这样一个系统的必需工具。
专注于实时处理的异步任务队列,同时也支持任务调度。
注意Celery 4.0支持Django 1.8和更高版本。对于Django 1.8之前的版本,请使用Celery 3.1。
1.2.为什么使用celery
- 可以用于解决异步任务和定时任务
- 使用方便简单
Celery是一个使用Python开发的分布式任务调度模块,因此对于大量使用Python构建的系统,可以说是无缝衔接,使用起来很方便。(python开发,使用方便)
Celery专注于实时处理任务,同时也支持任务的定时调度。因此适合实时异步任务定时任务等调度场景。(适用解决异步任务;定时任务)
Celery需要依靠RabbitMQ等作为消息代理,同时也支持Redis甚至是Mysql,MongoDB等,当然,官方默认推荐的是RabbitMQ。
1.3.celery的优点
- 简单:Celery 使用和维护都非常简单,并且不需要配置文件。
- 高可用:woker和client会在网络连接丢失或者失败时,自动进行重试。并且有的brokers 也支持“双主”或者“主/从”的方式实现高可用。
- 快速:单个的Celery进程每分钟可以处理百万级的任务,并且只需要毫秒级的往返延迟(使用 RabbitMQ, librabbitmq, 和优化设置时)
- 灵活: Celery几乎每个部分都可以扩展使用,自定义池实现、序列化、压缩方案、日志记录、调度器、消费者、生产者、broker传输等等。
1.4.Celery 特性
- 方便查看定时任务的执行情况, 如 是否成功, 当前状态, 执行任务花费的时间等.
- 可选 多进程, Eventlet 和 Gevent 三种模型并发执行.
- Celery 是语言无关的.它提供了python 等常见语言的接口支持.
1.5.Celery 扮演生产者和消费者的角色
- Celery Beat :
任务调度器. Beat 进程会读取配置文件的内容, 周期性的将配置中到期需要执行的任务发送给任务队列. - Celery Worker : 执行任务的``消费者`, 通常会在多台服务器运行多个消费者, 提高运行效率.
- Broker :
消息代理, 队列本身. 也称为消息中间件. 接受任务生产者发送过来的任务消息, 存进队列再按序分发给任务消费方(通常是消息队列或者数据库). - Producer :
任务生产者. 调用 Celery API , 函数或者装饰器, 而产生任务并交给任务队列处理的都是任务生产者. - Result Backend : 任务处理完成之后保存状态信息和结果, 以供查询,db
任务结果存储
Task result store用来存储Worker执行的任务的结果,Celery支持以不同方式存储任务的结果,包括AMQP, redis等
另外, Celery还支持不同的并发和序列化的手段
- 并发:Prefork, Eventlet, gevent, threads/single threaded
- 序列化:pickle, json, yaml, msgpack. zlib, bzip2 compression, Cryptographic message signing 等等
1.6.celery架构图

1.7. 产生任务的方式
- 发布者发布任务(WEB 应用)
- 任务调度按期发布任务(定时任务)
1.8.celery 依赖三个库: 这三个库, 都由 Celery 的开发者开发和维护.
billiard :基于 Python2.7 的 multisuprocessing 而改进的库, 主要用来提高性能和稳定性.librabbitmp :C 语言实现的 Python 客户端kombu :Celery 自带的用来收发消息的库, 提供了符合 Python 语言习惯的, 使用 AMQP 协议的高级借口.
1.9. celery的应用场景
celery是一个强大的 分布式任务队列的异步处理框架,它可以让任务的执行完全脱离主程序,甚至可以被分配到其他主机上运行。我们通常使用它来实现异步任务(async task)和定时任务(crontab)。
- 异步任务:将耗时操作任务提交给Celery去异步执行,比如发送短信/邮件、消息推送、音视频处理等等
- 定时任务:定时执行某件事情,比如每天数据统计
2.Celery执行流程

3.Celery的安装
你可以通过pip安装Celery:
pip install -U Celery # -U 表示 update
4.Celery执行异步任务
4.1.Celery的使用
创建一个python项目,由生产消费模型。我这命名Celerypro
- 启动celery (消费者)
- 监听事件
- 开启默认并发量
- 消息中间件,创建队列
- 等待事件触发
- 海量数据 (生产者)
- 调用消费者方法(自定义处理数据方法).delay(参数)
- 创建与消息中间件中的队列连接
- 执行自定义处理方法
- celery返回存储结果查询key(无论成功与否)
- celery将结果存储存储结果中(查询可知)
- 消费者在合适时机,通过key,前往存储结果,查询结果,获取数据
4.1.1消费者:celery_task.py
celery_task,处理外部传递来的海量数据,处理(task)不就是消费者?
import time
import celery
backend = 'redis://:root@127.0.0.1:6379/1' # 异步结果(backend) results
broker = 'redis://:root@127.0.0.1:6379/2' # 消息中间件(broker) transport
cel = celery.Celery('test', backend=backend, broker=broker)
@cel.task
def send_email(name):
print("向%s发送邮件..." % name)
time.sleep(5)
print("向%s发送邮件完成" % name)
return "ok" # 结果存储在存储结果中(ps:redis)
@cel.task
def send_msg(name):
print("向%s发送短信..." % name)
time.sleep(5)
print("向%s发送短信完成" % name)
return "ok"
# 消费者(处理生产者生产的数据),如何处理:一个个的处理方法函数
# Usage: celery [OPTIONS] COMMAND [ARGS]...
# celery -A celery_task worker -l info
# - 中间经过:
# - celery 通过命令连接 消息中间件(redis),创建队列,监听此队列
# - 启动多个worker 监听任务
# redis 细分16个库(0-15)
# 链接格式:'redis://:密码@ip:port/part'
# 添加@cel.task装饰器,send_email方法就是celery异步任务;中间就会有一个delay方法
# 当你在生产者环境中调用delay方法时,它会执行:
# - 生产者连接消息中间件中由results创建的队列
# - 连接传参:函数名称 + 函数参数
4.1.2生产者:produce_task.py
from celery_task import send_msg, send_email
result1 = send_email.delay('yang@gmail.com')
# <class 'celery.result.AsyncResult'>
print(type(result1), result1)
result2 = send_msg.delay('yang@gmail.com')
# <class 'str'>
print(type(result2.id), result2.id)
# 最好转化为str, celery 返回的不是结果数据,而是通过返回的结果作为钥匙去redis(存储结果)取。
# 生产者(模仿工作中大量的并发数据,同时发送)
# 需要将celery任务,也就是处理数据的函数导入,并执行函数的delay方法:result = func.delay(args,kwargs)
# 在生产者中只要调用消费者中的delay方法
# - 生成者会链接到由celery创建的队列中。
# - 找到处理此生产的函数,并创建成为一个worker
# - 将函数处理的结果存储到存储结果中(ps:redis)
# - 并返回获取结果的key (ps:redis 查询的key)
4.1.3执行celery异步文件
控制台命令行执行:
celery -A celery_app worker -l info
options
-A APPLICATION
-l 日志输出
worker 表示消息中间件
prefork:默认可能会报错,报错安装: eventlet模块,并在执行时,添加: -P eventlet (携程)
'-P' or '--pool': 'prefork', 'eventlet', 'gevent', 'solo', 'processes', 'threads'
4.1.4获取存储结果
from celery.result import AsyncResult
from celery_task import cel # 消费者中创建的cel
# AsyncResult(id=celery_result.id, app=func_in_app)
async_result = AsyncResult(id="3177abcc-a949-46b7-afaa-60234b1e7a4d", 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('任务已经开始被执行')
4.2多任务结构
创建一个python项目,由生产消费模型。我这命名CeleryTask
4.2.1目录结构

4.2.2celery.py
文件名可以自拟,不一定要用celery
# 此位celery的配置文件
import celery
# 准备好消息中间件,存储结果的 path
border = 'redis://:root@127.0.0.1:6379/1'
backend = 'redis://:root@127.0.0.1:6379/2'
# 包含以下两个任务文件.去相应的py文件中找任务,对多个任务做分类(path,split='.')
include = [
'celery_tasks.async_task01',
'celery_tasks.async_task02',
]
# 创建Celery app
app = celery.Celery('celery_app_name', backend=backend, broker=border, include=include)
# 设置当前时区
app.conf.timezone = 'Asia/Shanghai'
# 是否使用utc时间
app.conf.enable_utc = False
4.2.3async_task01.py
import time
from .celery import app
@app.task
def async_task01_fun(name):
time.sleep(5)
print('async_task01_fun(name):', name)
print("完成向%s发送邮件任务" % name)
return __name__
4.2.4async_task02.py
import time
from .celery import app
@app.task
def async_task02_fun(name):
time.sleep(5)
print('async_task02_fun(name):', name)
print("完成向%s发送短信任务" % name)
return __name__
4.2.5produce_task.py
# 导入生产的任务
from celery_tasks import async_task01, async_task02
result = async_task01.async_task01_fun.delay('yang')
print(result.id)
result2 = async_task02.async_task02_fun.delay('yang')
print(result2.id)
4.2.6check_result.py
# 导入celery中的异步返回,与celery app对象
from celery.result import AsyncResult
from celery_tasks.celery import app
async_result = AsyncResult(id='10b21178-6ba0-464a-8be5-d6b5586ea290', app=app)
if async_result.successful:
"""success"""
print(async_result.get())
# 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('任务已经开始被执行')
# status:PENDING,STARTED,RETRY,FAILURE,SUCCESS
4.2.7执行异步文件命令
celery -A celery_tasks worker -l info -P eventlet
5.Celery执行定时任务

redis命令
redis:
- 登录
redis-cli -h host -p port -a password
- 切换区域(0-15)
select idnex
- 查询所有键
keys *
- 查看key对应值的类型
type key
- 查看redis中list类型的所有数据
LRANGE KEY START STOP
LRANGE KEY 0 -1
-删除redis中的key
delete key
目录结构

5.1 config.py
xxx.config.beat_schedule: 定时任务相关的调度器 ,xxx是自己创建按的celery_app
from datetime import timedelta
from celery import Celery
app = Celery(
main='app_name',
broker='redis://:root@127.0.0.1:6379/1',
backend='redis://:root@127.0.0.1:6379/2',
include=[ # 用于解决消费者方的路径问题
'auto_task01',
'auto_task02'
]
)
# celery schedule ( 时间表 ) 定时向消息中间件中的队列中插入任务
app.conf.beat_schedule = {
# 自定义key(尽量见名知意)
"add-every-5-second": {
'task': 'auto_task01.auto_task01', # 定时任务的路径,通过'.'实现分割,用于解决生产者方的问题
'schedule': timedelta(seconds=5), # schedule(日程): 间隔时间
'args': ('yang',) # 方法args参数
},
"add-every-3-second": {
'task': 'auto_task02.auto_task02',
'schedule': timedelta(seconds=3),
'args': ('alex',),
'kwargs': {
'name': 'yang',
'age': 18
}
},
}
5.2auto_task01.py
import time
from config import app
@app.task
def auto_task01(name):
print(name)
time.sleep(5)
return __name__
5.3auto_task02.py
import time
from config import app
@app.task
def auto_task02(*args, **kwargs):
print("args:", args)
print("kwargs:", kwargs)
time.sleep(5)
return __name__
5.4redis_handler.py
import redis
conn = redis.Redis(host='localhost', port=6379, db=1, password='root') # broker
result = conn.lrange('celery', 0, -1) # celery 存储在redis 数据库中的 消息中间件中的任务(消息)队列
print('type: celery', conn.type('celery')) # 查看celery 存储的格式类型:list
for task in result:
print('task: ', task, end='\n\n')
# 解决”历史遗留问题“
conn.delete('celery')
result = conn.lrange('celery', 0, -1)
print(result)
执行定时任务
处理定时任务:worker
celery -A config worker -l info -P eventlet
添加定时任务:beat
Celery Beat进程会读取配置文件的内容,周期性的将配置中到期需要执行的任务发送给任务队列
celery -A config beat -l info
6.Django中使用Celery异步任务
目录结构

关键步骤
app.config_from_object # 导入配置文件配置项
ps:
app = Celery()
app.config_from_object(path_Split_dot)
<=> app = Celery(config)
# 加载任务,默认任务存放位置为:xxx.tasks 文件,
app.autodiscover_tasks(packages:list[str]=None, related_name='tasks', force=False)
ps:
packages = ['celery_tasks.tasks.asy.sms', 'celery_tasks.tasks.asy.email']
app.autodiscover_tasks(packages)
<=> 默认将sms目录与email目录下的tasks文件中的方法作为celery_task
#使用Django时要通知Django当前使用的是哪个配置文件;可以改变环境变量 DJANGO_SETTINGS_MODULE 实现这一点(环境变量),让django知道使用的是哪个配置文件。
import os
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'DCelery.settings')
6.1Celery 消费者区域
6.1.1main.py
celery的主配置文件。
import os
from celery import Celery
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'DCelery.settings') # 务必在创建celery app之前(之后操作数据库相关的会找不到)
app = Celery('DCelery')
# 导入配置文件
path = 'celery_tasks.config'
app.config_from_object(path)
# 导入任务文件(模块/包)
packages = ['celery_tasks.tasks.asy.sms', 'celery_tasks.tasks.asy.email',
'celery_tasks.tasks.schedule']
app.autodiscover_tasks(packages)
6.1.2config.py
方便解耦,部分配置文件存放。
# 配置文件(解耦)(名字固定)
broker_url = 'redis://:root@127.0.0.1:6379/1'
result_backend = 'redis://:root@127.0.0.1:6379/2'
6.1.3tasks任务
celery的任务就一个要求:任务文件(模块)的名字必须是tasks
# path:celery_tasks/tasks/asy/sms/tasks.py
import time
import random
from celery_tasks.main import app
@app.task
def send_sms(name):
sms = random.randrange(1000, 10000)
print('正在向%s发送短信....' % name)
time.sleep(5)
print('%s已经接受到短信....' % name)
return sms
# path:celery_tasks/tasks/asy/email/tasks.py
@app.task
def send_email(name):
print('正在向%s发送短信....' % name)
time.sleep(4)
print('%s已经接受到短信....' % name)
return True
# path:celery_tasks/tasks/shedule/tasks.py
@app.task
def clear_db_orders(name):
order_id = random.randrange(1000, 10000)
print('正在清楚%s的订单信息....' % name)
time.sleep(5)
print('已经清楚完成%s的订单信息....' % name)
return order_id
6.2 Django 生产者区域
其实没什么变化,直接导入使用。
views.py
from datetime import datetime
from datetime import timedelta
from django.http import HttpResponse
from celery_tasks.tasks.asy.email.tasks import send_email
from celery_tasks.tasks.asy.sms.tasks import send_sms
from celery_tasks.tasks.schedule.tasks import clear_db_orders
def test(request):
# 异步任务(同步操作则会等待发送完成之后加载)
name = 'yang'
send_sms.delay(name)
send_email.delay(name)
# 定时任务
ctime = datetime.now()
utc_time = datetime.utcfromtimestamp(ctime.timestamp())
delay_time = timedelta(seconds=10)
etc_time = utc_time + delay_time
clear_db_orders.apply_async(args=('yang',), etc=etc_time)
return HttpResponse('OK')
6.3命令行启动celery
在django中celery充当的是一个消费者的角色,所以只需要使用worker模式即可。(视情况而定)
celery -A celery_tasks.main worker [-l info -P eventlet -c 5] # []中的可以选填
django中还有django-celery模块。
7.Django中使用Celery定时任务
如果想要在django中使用定时任务功能同样是靠beat完成任务发送功能,当在Django中使用定时任务时,需要安装django-celery-beat插件。以下将介绍使用过程。
7.1安装配置
1.beat插件安装
pip3 install django-celery-beat
2.注册APP
INSTALLED_APPS = [
....
'django_celery_beat',
]
3.数据库变更
python manage.py migrate
python3 manage.py migrate django_celery_beat
4.分别启动woker和beta
#启动beta 调度器使用数据库
celery -A proj beat -l info --scheduler django_celery_beat.schedulers:DatabaseScheduler
#启动woker
celery worker -A taskproj -l info
5.配置admin
在urls.py写入:
# urls.py
from django.conf.urls import url
from django.contrib import admin
urlpatterns = [
url(r'^admin/', admin.site.urls),
]
6.创建用户
python3 manage.py createsuperuser
7.登录admin进行管理
并且还可以看到我们上次使用orm作为结果存储的表。http://127.0.0.1:8000/admin/login/

使用示例:


查看结果:

小bug: 再次支付时,应该是直接获取数据库中的订单信息直接去结账,而不是重新生成订单。
警告提示
这里的debug是说 :userwarning:使用settings.debug会导致内存泄漏,请不要在生产环境中使用此设置!
[2022-09-15 15:06:32,783: WARNING/MainProcess] D:\Python\DCelery\lib\site-packages\celery\fixups\django.py:203: UserWarning: Using settings.DEBUG leads to a memory
leak, never use this setting in production environments!
warnings.warn('''Using settings.DEBUG leads to a memory...''')
django.conf.settings
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
# 所以我们就要在settings配置文件中把debug改为False(生产请务必使用)
8.Celery commands params
(Celery) E:\StudyClass\Python_Frame\Celery\CeleryTimeTask\CeleryTimeTask>celery
Usage: celery [OPTIONS] COMMAND [ARGS]...
Options:
-A, --app APPLICATION # APP
-b, --broker TEXT # broker
-c # 并发量
-P, --pool # 并发类型
Commands:
beat Start the beat periodic task scheduler. # 生产者(每隔多少时间向队列中插入任务)
worker Start worker instance. # 消费者(从队列中获取任务,并执行,存储结果,返回id)

浙公网安备 33010602011771号