celery
一、什么是Celery
1.1、celery是什么
Celery是一个简单、灵活且可靠的,处理大量消息的分布式系统,专注于实时处理的异步任务队列,同时也支持任务调度。

Celery的架构由三部分组成,消息中间件(message broker),任务执行单元(worker)和任务执行结果存储(task result store)组成。
消息中间件
Celery本身不提供消息服务,但是可以方便的和第三方提供的消息中间件集成。包括,RabbitMQ, Redis等等
任务执行单元
Worker是Celery提供的任务执行的单元,worker并发的运行在分布式的系统节点中。
任务结果存储
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.2、使用场景
celery是一个强大的 分布式任务队列的异步处理框架,它可以让任务的执行完全脱离主程序,甚至可以被分配到其他主机上运行。我们通常使用它来实现异步任务(async task)和定时任务(crontab)。
异步任务:将耗时操作任务提交给Celery去异步执行,比如发送短信/邮件、消息推送、音视频处理等等
定时任务:定时执行某件事情,比如每天数据统计
1.3、Celery具有以下优点
Simple(简单)
Celery 使用和维护都非常简单,并且不需要配置文件。
Highly Available(高可用)
woker和client会在网络连接丢失或者失败时,自动进行重试。并且有的brokers 也支持“双主”或者“主/从”的方式实现高可用。
Fast(快速)
单个的Celery进程每分钟可以处理百万级的任务,并且只需要毫秒级的往返延迟(使用 RabbitMQ, librabbitmq, 和优化设置时)
Flexible(灵活)
Celery几乎每个部分都可以扩展使用,自定义池实现、序列化、压缩方案、日志记录、调度器、消费者、生产者、broker传输等等。
1.4、Celery安装
你可以安装Celery通过Python包管理平台(PyPI)或者源码安装
使用pip安装:
$ pip install -U Celery -i https://pypi.tuna.tsinghua.edu.cn/simple
或着:
$ sudo easy_install Celery
二、Celery执行异步任务
2.1、基本使用
创建项目celerypro
创建异步任务执行文件celery_task:
import celery
import time
# backend='redis://127.0.0.1:6379/1' # 不带密码
# broker='redis://127.0.0.1:6379/2'
# 带密码连接redis:redis会分为16个库,编号 0-15 可以随意选择
backend = 'redis://:123456@127.0.0.1:6379/1' # task result store 结果存储位置
broker = 'redis://:123456@127.0.0.1:6379/2' # 消息中间件
cel = celery.Celery('test', backend=backend, broker=broker) # 任务名、结果存储在哪个数据库里、消息中间件
def sene_email(name):
print('向%s发送邮件...' % name)
time.sleep(5)
print('向%s发送邮件完成' % name)
return 'ok'
创建执行任务文件,produce_task.py:
from celery_task import send_email
result = send_email.delay("yuan")
print(result.id)
result2 = send_email.delay("alex")
print(result2.id)
注意,异步任务文件命令执行:
# celery 4.0 执行命令
celery worker -A celery_app_task -l info
# celery 5.0 执行命令
celery -A celery_task worker -l info
# 如果遇到 ValueError: not enough values to unpack (expected 3, got 0)
# 解决办法
celery -A celery_task worker -l info --pool=solo
创建py文件:result.py,查看任务执行结果,
from celery.result import AsyncResult
from celery_task import cel
async_result=AsyncResult(id="c6ddd5b7-a662-4f0e-93d4-ab69ec2aea5d", 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('任务已经开始被执行')
2.1、多任务结构

celery.py:
[
](javascript:void(0)😉
from celery import Celery
cel = Celery('celery_demo',
broker='redis://127.0.0.1:6379/1',
backend='redis://127.0.0.1:6379/2',
# 包含以下两个任务文件,去相应的py文件中找任务,对多个任务做分类
include=['celery_tasks.task01',
'celery_tasks.task02'
])
# 时区
cel.conf.timezone = 'Asia/Shanghai'
# 是否使用UTC
cel.conf.enable_utc = False
task01.py,task02.py:
#task01
import time
from celery_tasks.celery import cel
@cel.task
def send_email(res):
time.sleep(5)
return "完成向%s发送邮件任务"%res
#task02
import time
from celery_tasks.celery import cel
@cel.task
def send_msg(name):
time.sleep(5)
return "完成向%s发送短信任务"%name
produce_task.py:
from celery_tasks.task01 import send_email
from celery_tasks.task02 import send_msg
# 立即告知celery去执行test_celery任务,并传入一个参数
result = send_email.delay('yuan')
print(result.id)
result = send_msg.delay('yuan')
print(result.id)
check_result.py:
from celery.result import AsyncResult
from celery_tasks.celery import cel
async_result = AsyncResult(id="562834c6-e4be-46d2-908a-b102adbbf390", 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('任务已经开始被执行')
# celery4.4开启work:
celery worker -A celery_task -l info -P eventlet
# 添加任务(执行produce_task.py),检查任务执行结果(执行check_result.py)
# celery 5.0 开启work
celery -A celery_tasks worker -l info -P eventlet
三、Celery执行定时任务
在简单模式下设定时间让celery执行一个定时任务,produce_task.py:
from celery_task import send_email
from datetime import datetime
# 方式一
v1 = datetime(2022, 5, 20, 20, 12, 00)
print(v1)
v2 = datetime.utcfromtimestamp(v1.timestamp())# 将本地时间转为国标时间
print(v2)
result = send_email.apply_async(args=["egon",], eta=v2)
print(result.id)
# 方式二
ctime = datetime.now()
# 默认用utc时间
utc_ctime = datetime.utcfromtimestamp(ctime.timestamp()) # 国标当前时间
from datetime import timedelta # 时差类
time_delay = timedelta(seconds=10) # 时差加10s
task_time = utc_ctime + time_delay
# 使用apply_async并设定时间
result = send_email.apply_async(args=["egon"], eta=task_time) # 到达定时时间后推迟10s执行任务
print(result.id)
多任务结构中celery.py修改如下:
# celery 配置文件
from celery import Celery
from datetime import timedelta
cel = Celery('celery_demo',
broker='redis://:123456@127.0.0.1:6379/1',
backend='redis://:123456@127.0.0.1:6379/2',
# 包含以下两个任务文件,去相应的py文件中找任务,对多个任务做分类
include=['celery_tasks.task01',
'celery_tasks.task02'
])
# 时区
cel.conf.timezone = 'Asia/Shanghai'
# 是否使用UTC
cel.conf.enable_utc = False
# beat_schedule 定时任务调度器
cel.conf.beat_schedule = {
# 名字随意命名
'add-every-10-seconds': {
# 执行tasks1下的test_celery函数
'task': 'celery_tasks.task01.send_email',
# 每隔6秒执行一次
# 'schedule': 1.0, # 每1s执行一次
# 'schedule': crontab(minute="*/1"), # 每一分钟执行一次
'schedule': timedelta(seconds=6), # 每6s之后
# 传递参数
'args': ('张三',)
},
# 'add-every-12-seconds': {
# 'task': 'celery_tasks.task01.send_email',
# 每年4月11号,8点42分执行
# 'schedule': crontab(minute=42, hour=8, day_of_month=11, month_of_year=4),
# 'args': ('张三',)
# },
}
celery beat 向中间件插入任务;先启动调度器,在启动woker执行任务
# celery -A celery_task worker -l info -C 每次处理任务的并发数 5个
# celery5.0启动调度器:celery -A celery_tasks beat 添加任务调度器,向中间件里添加任务;如果停了woker但没有挺beat,中间件里会产生大量历史遗留任务,下次启动worker时就会自动执行,所以先要删除历史遗留woker再去执行
# celery4.4启动 Beat 程序 celery beat -A proj<br># Celery Beat进程会读取配置文件的内容,周期性的将配置中到期需要执行的任务发送给任务队列
# 选择redis库: select 1
四、Django中使用celery
项目根目录创建celery包,目录结构如下:
mycelery/
├── config.py
├── __init__.py
├── main.py
└── sms/
├── __init__.py
├── tasks.py
配置文件config.py:
broker_url = 'redis://127.0.0.1:6379/15'
result_backend = 'redis://127.0.0.1:6379/14'
任务文件tasks.py:
# celery的任务必须写在tasks.py的文件中,别的文件名称不识别!!!
from mycelerys.main import app
import time
import logging
log = logging.getLogger("django")
@app.task # name表示设置任务的名称,如果不填写,则默认使用函数名做为任务名
def send_sms(mobile):
"""发送短信"""
print("向手机号%s发送短信成功!"%mobile)
time.sleep(5)
return "send_sms OK"
@app.task # name表示设置任务的名称,如果不填写,则默认使用函数名做为任务名
def send_sms2(mobile):
print("向手机号%s发送短信成功!" % mobile)
time.sleep(5)
return "send_sms2 OK"
最后在main.py主程序中对django的配置文件进行加载
# 主程序
import os
from celery import Celery
# 创建celery实例对象
app = Celery("sms")
# 把celery和django进行组合,识别和加载django的配置文件
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'djangocelery.settings')
import django
django.setup()
# 通过app对象加载配置
app.config_from_object("mycelery.config")
# 加载任务
# 参数必须必须是一个列表,里面的每一个任务都是任务的路径名称
# app.autodiscover_tasks(["任务1","任务2"])
app.autodiscover_tasks(["mycelery.sms", ])
# 启动Celery的命令
# 强烈建议切换目录到mycelery根目录下启动
# celery -A mycelery.main worker --loglevel=info --pool=solo
Django视图调用:
from django.shortcuts import render
# Create your views here.
from django.shortcuts import render,HttpResponse
from mycelerys.sms.tasks import send_sms,send_sms2
from datetime import timedelta
from datetime import datetime
def test(request):
################################# 异步任务
# 1. 声明一个和celery一模一样的任务函数,但是我们可以导包来解决
# send_sms.delay("110")
# send_sms2.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_sms.apply_async(["911", ], eta=task_time)
# print(result.id)
return HttpResponse('ok')
五、基本使用
使用celery第一件要做的最为重要的事情是需要先创建一个Celery实例对象,我们一般叫做celery应用对象,或者更简单直接叫做一个app。app应用对象是我们使用celery所有功能的入口,比如启动celery、创建任务,管理任务,执行任务等.
celery框架有2种使用方式,一种是单独一个项目目录,另一种就是Celery集成到web项目框架中。
celery作为一个单独项目运行
例如,mycelery代码目录直接放在项目根目录下即可,路径如下:
服务端项目根目录/
└── mycelery/
├── settings.py # 配置文件
├── __init__.py
├── main.py # 入口程序
└── sms/ # 异步任务目录,这里拿发送短信来举例,一个类型的任务就一个目录
└── tasks.py # 任务的文件,文件名必须是tasks.py!!!每一个任务就是一个被装饰的函数,写在任务文件中
main.py,代码:
from celery import Celery
# 实例化celery应用,参数一般为项目应用名
app = Celery("luffycity")
# 通过app实例对象加载配置文件
app.config_from_object("mycelery.settings")
# 注册任务, 自动搜索并加载任务
# 参数必须必须是一个列表,里面的每一个任务都是任务的路径名称
# app.autodiscover_tasks(["任务1","任务2",....])
app.autodiscover_tasks(["mycelery.sms","mycelery.email"])
# 启动Celery的终端命令
# 强烈建议切换目录到项目的根目录下启动celery!!
# celery -A mycelery.main worker --loglevel=info
配置文件settings.py,代码:
# 任务队列的链接地址
broker_url = 'redis://127.0.0.1:6379/14'
# 结果队列的链接地址
result_backend = 'redis://127.0.0.1:6379/15'
关于配置信息的官方文档:https://docs.celeryproject.org/en/master/userguide/configuration.html
创建任务文件sms/tasks.py,任务文件名必须固定为"tasks.py",并创建任务,代码:
from ..main import app
@app.task
def send_sms1():
"""没有任何参数的异步任务"""
print('任务:send_sms1执行了...')
@app.task(name="send_sms2")
def send_sms2(mobile, code):
"""有参数的异步任务"""
print(f'任务:send_sms2执行了...mobile={mobile}, code={code}')
@app.task
def send_sms3():
"""有结果的异步任务"""
print('任务:send_sms3执行了...')
return 100
@app.task(name="send_sms4")
def send_sms4(x,y):
"""有结果的异步任务"""
print('任务:send_sms4执行了...')
return x+y
接下来,我们运行celery。
cd ~/Desktop/luffycity/luffycityapi
# 普通的运行方式[默认多进程,卡终端,按CPU核数+1创建进程数]
# ps aux|grep celery
celery -A mycelery.main worker --loglevel=info
# 启动多工作进程,以守护进程的模式运行[一个工作进程就是4个子进程]
# 注意:pidfile和logfile必须以绝对路径来声明
celery multi start worker -A mycelery.main -E --pidfile="/home/moluo/Desktop/luffycity/luffycityapi/logs/worker1.pid" --logfile="/home/moluo/Desktop/luffycity/luffycityapi/logs/celery.log" -l info -n worker1
celery multi start worker -A mycelery.main -E --pidfile="/home/moluo/Desktop/luffycity/luffycityapi/logs/worker2.pid" --logfile="/home/moluo/Desktop/luffycity/luffycityapi/logs/celery.log" -l info -n worker2
# 关闭运行的工作进程
celery multi stop worker -A mycelery.main --pidfile="/home/moluo/Desktop/luffycity/luffycityapi/logs/worker1.pid" --logfile="/home/moluo/Desktop/luffycity/luffycityapi/logs/celery.log"
celery multi stop worker -A mycelery.main --pidfile="/home/moluo/Desktop/luffycity/luffycityapi/logs/worker2.pid" --logfile="/home/moluo/Desktop/luffycity/luffycityapi/logs/celery.log"
效果如下:

调用上面的异步任务,拿django的shell进行举例:
# 因为celery模块安装在了虚拟环境中,所以要确保进入虚拟环境
conda activate luffycity
cd ~/Desktop/luffycity/luffycityapi
python manage.py shell
# 调用celery执行异步任务
from mycelery.sms.tasks import send_sms1,send_sms2,send_sms3,send_sms4
mobile = "13312345656"
code = "666666"
# delay 表示马上按顺序来执行异步任务,在celrey的worker工作进程有空闲的就立刻执行
# 可以通过delay异步调用任务,可以没有参数
ret1 = send_sms1.delay()
# 可以通过delay传递异步任务的参数,可以按位置传递参数,也可以使用命名参数
# ret2 = send_sms.delay(mobile=mobile,code=code)
ret2 = send_sms2.delay(mobile,code)
# apply_async 让任务在后面指定时间后执行,时间单位:秒/s
# 任务名.apply_async(args=(参数1,参数2), countdown=定时时间,等待一段时间后执行)
ret4 = send_sms4.apply_async(kwargs={"x":10,"y":20},countdown=30)
# 根据返回结果,不管delay,还是apply_async的返回结果都一样的。
ret4.id # 返回一个UUID格式的任务唯一标志符,78fb827e-66f0-40fb-a81e-5faa4dbb3505
ret4.status # 查看当前任务的状态 SUCCESS表示成功! PENDING任务等待
ret4.get() # 获取任务执行的结果[如果任务函数中没有return,则没有结果,如果结果没有出现则会导致阻塞]
if ret4.status == "SUCCESS":
print(ret4.get())
接下来,我们让celery可以调度第三方框架的代码,这里拿django当成一个第三模块调用进行举例。
在main.py主程序中对django进行导包引入,并设置django的配置文件进行django的初始化。
import os,django
from celery import Celery
# 初始化django
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'luffycityapi.settings.dev')
django.setup()
# 初始化celery对象
app = Celery("luffycity")
# 加载配置
app.config_from_object("mycelery.config")
# 自动注册任务
app.autodiscover_tasks(["mycelery.sms","mycelery.email"])
# 运行celery
# 终端下: celery -A mycelery.main worker -l info
在需要使用django配置的任务中,直接加载配置,所以我们把注册的短信发送功能,整合成一个任务函数,mycelery.sms.tasks,代码:
from ..main import app
from ronglianyunapi import send_sms as send_sms_to_user
@app.task(name="send_sms1")
def send_sms1():
"""没有任何参数,没有返回结果的异步任务"""
print('任务:send_sms1执行了...')
@app.task(name="send_sms2")
def send_sms2(mobile, code):
"""有参数,没有返回结果的异步任务"""
print(f'任务:send_sms2执行了...mobile={mobile}, code={code}')
@app.task(name="send_sms3")
def send_sms3():
"""没有任何参数,有返回结果的异步任务"""
print('任务:send_sms3执行了...')
return 100
@app.task(name="send_sms4")
def send_sms4(x,y):
"""有结果的异步任务"""
print('任务:send_sms4执行了...')
return x+y
@app.task(name="send_sms")
def send_sms(tid, mobile, datas):
"""发送短信"""
print("发送短信")
return send_sms_to_user(tid, mobile, datas)
最终在django的视图里面,我们调用Celery来异步执行任务。
只需要完成2个步骤,分别是导入异步任务和调用异步任务。users/views.py,代码:
import random
from django_redis import get_redis_connection
from django.conf import settings
# from ronglianyunapi import send_sms
from mycelery.sms.tasks import send_sms
"""
/users/sms/(?P<mobile>1[3-9]\d{9})
"""
class SMSAPIView(APIView):
"""
SMS短信接口视图
"""
def get(self, request, mobile):
"""发送短信验证码"""
redis = get_redis_connection("sms_code")
# 判断手机短信是否处于发送冷却中[60秒只能发送一条]
interval = redis.ttl(f"interval_{mobile}") # 通过ttl方法可以获取保存在redis中的变量的剩余有效期
if interval != -2:
return Response({"errmsg": f"短信发送过于频繁,请{interval}秒后再次点击获取!", "interval": interval},status=status.HTTP_400_BAD_REQUEST)
# 基于随机数生成短信验证码
# code = "%06d" % random.randint(0, 999999)
code = f"{random.randint(0, 999999):06d}"
# 获取短信有效期的时间
time = settings.RONGLIANYUN.get("sms_expire")
# 短信发送间隔时间
sms_interval = settings.RONGLIANYUN["sms_interval"]
# 调用第三方sdk发送短信
# send_sms(settings.RONGLIANYUN.get("reg_tid"), mobile, datas=(code, time // 60))
# 异步发送短信
send_sms.delay(settings.RONGLIANYUN.get("reg_tid"), mobile, datas=(code, time // 60))
# 记录code到redis中,并以time作为有效期
# 使用redis提供的管道对象pipeline来优化redis的写入操作[添加/修改/删除]
pipe = redis.pipeline()
pipe.multi() # 开启事务
pipe.setex(f"sms_{mobile}", time, code)
pipe.setex(f"interval_{mobile}", sms_interval, "_")
pipe.execute() # 提交事务,同时把暂存在pipeline的数据一次性提交给redis
return Response({"errmsg": "OK"}, status=status.HTTP_200_OK)
上面就是使用celery并执行异步任务的第一种方式,适合在一些无法直接集成celery到项目中的场景。
cd /home/moluo/Desktop/luffycity
git add .
git commit -m "feature: celery作为一个单独项目运行,执行异步任务"
git push
Celery作为第三方模块集成到项目中
这里还是拿django来举例,目录结构调整如下:
luffycityapi/ # 服务端项目根目录
└── luffycityapi/ # 主应用目录
├── apps/ # 子应用存储目录
├ └── users/ # django的子应用
├ └── tasks.py # [新增]分散在各个子应用下的异步任务模块
├── settings/ # [修改]django的配置文件存储目录[celery的配置信息填写在django配置中即可]
├── __init__.py # [修改]设置当前包目录下允许外界调用celery应用实例对象
└── celery.py # [新增]celery入口程序,相当于上一种用法的main.py
luffycityapi/celery.py,主应用目录下创建cerley入口程序,创建celery对象并加载配置和异步任务,代码:
import os
from celery import Celery
# 必须在实例化celery应用对象之前执行
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'luffycityapi.settings.dev')
# 实例化celery应用对象
app = Celery('luffycityapi')
# 指定任务的队列名称
app.conf.task_default_queue = 'Celery'
# 也可以把配置写在django的项目配置中
app.config_from_object('django.conf:settings', namespace='CELERY') # 设置django中配置信息以 "CELERY_"开头为celery的配置信息
# 自动根据配置查找django的所有子应用下的tasks任务文件
app.autodiscover_tasks()
settings/dev.py,django配置中新增celery相关配置信息,代码:
# Celery异步任务队列框架的配置项[注意:django的配置项必须大写,所以这里的所有配置项必须全部大写]
# 任务队列
CELERY_BROKER_URL = 'redis://:123456@127.0.0.1:6379/14'
# 结果队列
CELERY_RESULT_BACKEND = 'redis://:123456@127.0.0.1:6379/15'
# 时区,与django的时区同步
CELERY_TIMEZONE = TIME_ZONE
# 防止死锁
CELERY_FORCE_EXECV = True
# 设置并发的worker数量
CELERYD_CONCURRENCY = 200
# 设置失败允许重试[这个慎用,如果失败任务无法再次执行成功,会产生指数级别的失败记录]
CELERY_ACKS_LATE = True
# 每个worker工作进程最多执行500个任务被销毁,可以防止内存泄漏,500是举例,根据自己的服务器的性能可以调整数值
CELERYD_MAX_TASKS_PER_CHILD = 500
# 单个任务的最大运行时间,超时会被杀死[慎用,有大文件操作、长时间上传、下载任务时,需要关闭这个选项,或者设置更长时间]
CELERYD_TIME_LIMIT = 10 * 60
# 任务发出后,经过一段时间还未收到acknowledge, 就将任务重新交给其他worker执行
CELERY_DISABLE_RATE_LIMITS = True
# celery的任务结果内容格式
CELERY_ACCEPT_CONTENT = ['json', 'pickle']
# 之前定时任务(定时一次调用),使用了apply_async({}, countdown=30);
# 设置定时任务(定时多次调用)的调用列表,需要单独运行SCHEDULE命令才能让celery执行定时任务:celery -A mycelery.main beat,当然worker还是要启动的
# https://docs.celeryproject.org/en/stable/userguide/periodic-tasks.html
from celery.schedules import crontab
CELERY_BEAT_SCHEDULE = {
"user-add": { # 定时任务的注册标记符[必须唯一的]
"task": "add", # 定时任务的任务名称
"schedule": 10, # 定时任务的调用时间,10表示每隔10秒调用一次add任务
# "schedule": crontab(hour=7, minute=30, day_of_week=1),, # 定时任务的调用时间,每周一早上7点30分调用一次add任务
}
}
luffycityapi/__init__.py,主应用下初始化,代码:
import pymysql
from .celery import app as celery_app
pymysql.install_as_MySQLdb()
__all__ = ['celery_app']
users/tasks.py,代码:
from celery import shared_task
from ronglianyunapi import send_sms as sms
# 记录日志:
import logging
logger = logging.getLogger("django")
@shared_task(name="send_sms")
def send_sms(tid, mobile, datas):
"""异步发送短信"""
try:
return sms(tid, mobile, datas)
except Exception as e:
logger.error(f"手机号:{mobile},发送短信失败错误: {e}")
@shared_task(name="send_sms1")
def send_sms1():
print("send_sms1执行了!!!")
django中的用户发送短信,就可以改成异步发送短信了。
users/views,视图中调用异步发送短信的任务,代码:
from .tasks import send_sms
send_sms.delay(settings.RONGLIANYUN.get("reg_tid"),mobile, datas=(code, time // 60))
users/views.py,异步发送信息的完整视图,代码:
import random
from django_redis import get_redis_connection
from django.conf import settings
# from ronglianyunapi import send_sms
# from mycelery.sms.tasks import send_sms
from .tasks import send_sms
"""
/users/sms/(?P<mobile>1[3-9]\d{9})
"""
class SMSAPIView(APIView):
"""
SMS短信接口视图
"""
def get(self, request, mobile):
"""发送短信验证码"""
redis = get_redis_connection("sms_code")
# 判断手机短信是否处于发送冷却中[60秒只能发送一条]
interval = redis.ttl(f"interval_{mobile}") # 通过ttl方法可以获取保存在redis中的变量的剩余有效期
if interval != -2:
return Response({"errmsg": f"短信发送过于频繁,请{interval}秒后再次点击获取!", "interval": interval},status=status.HTTP_400_BAD_REQUEST)
# 基于随机数生成短信验证码
# code = "%06d" % random.randint(0, 999999)
code = f"{random.randint(0, 999999):06d}"
# 获取短信有效期的时间
time = settings.RONGLIANYUN.get("sms_expire")
# 短信发送间隔时间
sms_interval = settings.RONGLIANYUN["sms_interval"]
# 调用第三方sdk发送短信
# send_sms(settings.RONGLIANYUN.get("reg_tid"), mobile, datas=(code, time // 60))
# 异步发送短信
send_sms.delay(settings.RONGLIANYUN.get("reg_tid"), mobile, datas=(code, time // 60))
# 记录code到redis中,并以time作为有效期
# 使用redis提供的管道对象pipeline来优化redis的写入操作[添加/修改/删除]
pipe = redis.pipeline()
pipe.multi() # 开启事务
pipe.setex(f"sms_{mobile}", time, code)
pipe.setex(f"interval_{mobile}", sms_interval, "_")
pipe.execute() # 提交事务,同时把暂存在pipeline的数据一次性提交给redis
return Response({"errmsg": "OK"}, status=status.HTTP_200_OK)
终端下先启动celery,在django项目根目录下启动。
cd ~/Desktop/luffycity/luffycityapi
# 1. 普通运行模式,关闭终端以后,celery就会停止运行
celery -A luffycityapi worker -l INFO
# 2. 启动多worker进程模式,以守护进程的方式运行,不需要在意终端。但是这种运行模型,一旦停止,需要手动启动。
celery multi start worker -A luffycityapi -E --pidfile="/home/moluo/Desktop/luffycity/luffycityapi/logs/worker1.pid" --logfile="/home/moluo/Desktop/luffycity/luffycityapi/logs/celery.log" -l info -n worker1
# 3. 关闭多worker进程模式
celery multi stop worker -A luffycityapi --pidfile="/home/moluo/Desktop/luffycity/luffycityapi/logs/worker1.pid"
还是可以在django终端下调用celery的
$ python manage.py shell
>>> from users.tasks import send_sms1
>>> res = send_sms1.delay()
>>> res = send_sms1.apply_async(countdown=15)
>>> res.id
'893c31ab-e32f-44ee-a321-8b07e9483063'
>>> res.status
'SUCCESS'
>>> res.result
关于celery中异步任务发布的2个方法的参数如下:
异步任务名.delay(*arg, **kwargs)
异步任务名.apply_async((arg,), {'kwarg': value}, countdown=60, expires=120)
定时任务的调用器启动,可以在运行了worker以后,使用以下命令:
cd ~/Desktop/luffycity/luffycityapi
celery -A luffycityapi beat
beat调度器关闭了,则定时任务无法执行,如果worker工作进程关闭了,则celery关闭,保存在消息队列中的任务就会囤积在那里。

celery还有一些高阶用法, 我们后面用到再提。
celery后面还可以使用supervisor进行后台托管运行。还可以针对任务执行的情况和结果,使用flower来进行监控。celery失败任务的重新尝试执行。
supervisor会在celery以外关闭了以后,自动重启celery。

浙公网安备 33010602011771号