Python celery原理及运行流程
1. Celery介绍
1.1 celery应用举例
- Celery 是一个 基于python开发的分布式异步消息任务队列,通过它可以轻松的实现任务的异步处理,如果你的业务场景中需要用到异步任务,就可以考虑使用celery;
- 你想对100台机器执行一条批量命令,可能会花很长时间 ,但你不想让你的程序等着结果返回,而是给你返回 一个任务ID,你过一段时间只需要拿着这个任务id就可以拿到任务执行结果, 在任务执行ing进行时,你可以继续做其它的事情;
- Celery 在执行任务时需要通过一个消息中间件来接收和发送任务消息,以及存储任务结果, 一般使用rabbitMQ or Redis。
1.2 Celery有以下优点
- 简单:一单熟悉了celery的工作流程后,配置和使用还是比较简单的;
- 高可用:当任务执行失败或执行过程中发生连接中断,celery 会自动尝试重新执行任务;
- 快速:一个单进程的celery每分钟可处理上百万个任务;
- 灵活: 几乎celery的各个组件都可以被扩展及自定制。
1.3 Celery 特性
- 方便查看定时任务的执行情况, 如 是否成功, 当前状态, 执行任务花费的时间等.
- 可选 多进程, Eventlet 和 Gevent 三种模型并发执行.
- Celery 是语言无关的.它提供了python 等常见语言的接口支持.
参考链接: https://www.cnblogs.com/xiaonq/p/11166235.html#i1
2. celery 组件
2.1 Celery 扮演生产者和消费者的角色
- Celery Beat : 任务调度器. Beat 进程会读取配置文件的内容, 周期性的将配置中到期需要执行的任务发送给任务队列.
- Celery Worker : 执行任务的消费者, 通常会在多台服务器运行多个消费者, 提高运行效率.
- Broker : 消息代理, 队列本身. 也称为消息中间件. 接受任务生产者发送过来的任务消息, 存进队列再按序分发给任务消费方(通常是消息队列或者数据库).
- Producer : 任务生产者. 调用 Celery API , 函数或者装饰器, 而产生任务并交给任务队列处理的都是任务生产者.
- Result Backend : 任务处理完成之后保存状态信息和结果, 以供查询.
2.2 celery架构图
2.3 产生任务的方式
- 发布者发布任务(WEB 应用)
- 任务调度按期发布任务(定时任务)
2.4 celery 依赖三个库: 这三个库, 都由 Celery 的开发者开发和维护.
billiard
: 基于 Python2.7 的 multisuprocessing 而改进的库, 主要用来提高性能和稳定性.librabbitmp :
C 语言实现的 Python 客户端kombu :
Celery 自带的用来收发消息的库, 提供了符合 Python 语言习惯的, 使用 AMQP 协议的高级接口.
参考链接: https://www.cnblogs.com/xiaonq/p/11166235.html#i2
3.celery配置与基本使用
3.1 安装celery
pip install celery @
https://github.com/celery/celery/tarball/master
3.2 新建celery/main.py
配置celery
# celery_task/main.py
import os
from celery import Celery
# 定义celery实例, 需要的参数, 1, 实例名, 2, 任务发布位置, 3, 结果保存位置
app = Celery('mycelery',
broker='redis://127.0.0.1:6379/14', # 任务存放的地方
backend='redis://127.0.0.1:6379/15') # 结果存放的地方
@app.task
def add(x, y):
return x + y
4.测试celery
启动celery命令
'''1.启动celery'''
#1.1 单进程启动celery
celery -A main worker -l INFO
#1.2 celery管理
celery multi start celery_test -A celery_test
-l debug --autoscale=50,5 # celery并发
数:最多50个,最少5个
ps auxww|grep "celery worker"|grep -v grep|awk
'{print $2}'|xargs kill -9 # 关闭所有
celery进程
5. 使用celery异步发送短信
5.1 在celery_task/mian.py
中添加发送短信函数
# celery项目中的所有导包地址, 都是以
CELERY_BASE_DIR为基准设定.
# 执行celery命令时, 也需要进入CELERY_BASE_DIR目录执行.
CELERY_BASE_DIR =
os.path.dirname(os.path.abspath(__file__))
@app.task(bind=True)
def send_sms_code(self, mobile, datas):
sys.path.insert(0, os.path.join(CELERY_BASE_DIR, '../syl'))
# 在方法中导包
from libs.rl_sms import send_message
# time.sleep(5)
try:
# 用 res 接收发送结果, 成功是:0, 失败是:-1
res = send_message(mobile, datas)
except Exception as e:
res = '-1'
if res == '-1':
# 如果发送结果是 -1 就重试.
self.retry(countdown=5, max_retries=3, exc=Exception('短信发送失败'))
5.2 在verifications/views.py
中添加celery发送短信试图函数
class SmsCodeView(APIView):
"""使用apiview的限流"""
# 1. 所有人可以访问
permission_classes = (AllowAny,)
def post(self, request):
# 1. 获取参数
phone = request.data.get('phone') # 手机号
image_code = request.data.get('image_code') # 图片验证码
image_code_uuid = request.data.get('image_code_uuid') # 前端生成的uuid
# 2. 检查参数
if not all([phone, image_code, image_code_uuid]):
return Response({"code": 999, "msg": "参数不全"})
if not re.match(r'^1[3456789]\d{9}$', phone):
return Response({"code": 999, "msg": "手机号码不正确"})
# 3. 检查是否发送
redis_client = get_redis_connection('img_code')
phone_exists = redis_client.get(phone)
if phone_exists:
return Response({"code": 999, "msg": "频繁发送, 请稍后再试"})
# 验证图形验证码
redis_image_code = redis_client.get(image_code_uuid) # bytes
if redis_image_code:
# bytes 转成 string
redis_image_code = redis_image_code.decode()
# 比较用户提供的图片内容是否和redis中保存的一致
if image_code.upper() != redis_image_code:
return Response({'code': 999, 'msg': '图片验证码不正确'})
# 4. 发送
code = '%06d' % random.randint(0, 999999) # 随机6位验证码
from syl.settings import BASE_DIR
sys.path.insert(0, os.path.join(BASE_DIR, '../celery_task'))
from main import send_sms_code # 必须这么写, 从main中导包
send_sms_code.delay(phone, (code, "5"))
print(code)
# 5.使用 pipeline 批量操作
pl = redis_client.pipeline() # 实例化pipeline对象
pl.setex(phone, 60 * 5, code) # 存储phone:code, 5分钟有效期
pl.delete(image_code_uuid) # 从redis中删除这个图片验证码, 以防再次被使用
pl.execute()
# 6. 返回结果
return Response({"code": 0, "msg": "短信发送成功"})
5.3 添加路由
xurlpatterns = [
path('sms_codes/',
views.SmsCodeView.as_view()),
]
6.测试接口
- 接口URL
http://192.168.56.100:8888/user/sms_codes/
- 请求携带参数
{
"phone": 15303478492,
"image_code":"aed3", # 前端生成的 图形验证码
"image_code_uuid":"de8edce2-fc9f-11ea-9325-005056c00008" # 前端生成的uuid
}