IT策士 10余年一线大厂经验,专注 IT 思维、架构、职场进阶。我也会在其它的平台持续发布最新文章,助你少走弯路。
系列写到这里,我们电商平台的核心业务已经跑通:用户注册、商品浏览、购物车、下单、支付、订单管理。但有一个体验问题一直存在------发送邮件和短信是在主请求线程里同步执行的。也就是说,用户注册时点击提交,后端要等邮件发完(哪怕只是写到控制台)才返回响应;下单成功后也要等短信发送完毕。这在开发阶段看不出来,但到了生产环境,网络延迟或邮件服务器慢都会让用户多等好几秒,严重影响体验。
解决这个问题的标准做法是:把耗时的 IO 操作从主线程剥离,交给异步任务队列处理 。Django 生态中最成熟的选择就是 Celery。今天我们就来给项目装上 Celery,改造邮件和短信的发送逻辑,让它们真正"后台运行",前端秒级响应。
一、为什么选择 Celery + Redis?
1.1 什么是 Celery?
Celery 是一个基于 Python 的分布式任务队列框架。它的核心概念:
工作流程:视图 → 把任务放入 Broker → Worker 取出执行 → 视图立刻返回,无需等待。
1.2 为什么选 Redis 做 Broker?
-
我们的项目第 24 篇还会用 Redis 做缓存,一举两得;
-
Redis 轻量级,学习成本低;
-
开发环境完全够用,生产环境中小规模也足够。
二、安装 Redis 和 Celery
2.1 安装 Redis
macOS:
bash
brew install redis
brew services start redis
Ubuntu/Debian:
bash
sudo apt update
sudo apt install redis-server
sudo systemctl start redis
Windows:
从 Redis for Windows 下载 msi 安装包,安装后会自动启动服务。
验证 Redis 是否启动:
控制台输出:
如果返回 PONG,说明 Redis 正常运行。
2.2 安装 Celery
进入项目虚拟环境:
bash
pip install celery[redis]
这条命令会同时安装 Celery 和它所需的 Redis 客户端依赖。
控制台输出:
bash
Collecting celery[redis]
Downloading celery-5.3.x-py3-none-any.whl
...
Successfully installed celery-5.3.x ...
验证安装:
控制台输出:
三、配置 Celery
3.1 创建 celery.py
在项目配置目录 django_ecommerce/ 下新建 celery.py:
bash
import os
from celery import Celery
# 设置 Django 的默认 settings 模块
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django_ecommerce.settings')
app = Celery('django_ecommerce')
# 从 Django settings 中加载 Celery 配置,所有配置项以 CELERY_ 开头
app.config_from_object('django.conf:settings', namespace='CELERY')
# 自动发现所有已注册 app 下的 tasks.py 文件
app.autodiscover_tasks()
3.2 修改 init.py
编辑 django_ecommerce/__init__.py,确保 Django 启动时加载 Celery:
bash
from .celery import app as celery_app
__all__ = ('celery_app',)
3.3 在 settings.py 中增加 Celery 配置
在 django_ecommerce/settings.py 末尾添加:
bash
# ==================== Celery 配置 ====================
# Redis 作为消息代理
CELERY_BROKER_URL = 'redis://127.0.0.1:6379/0'
# 结果存储后端(可选)
CELERY_RESULT_BACKEND = 'redis://127.0.0.1:6379/1'
# 接受的内容类型
CELERY_ACCEPT_CONTENT = ['json']
# 序列化方式
CELERY_TASK_SERIALIZER = 'json'
# 结果序列化方式
CELERY_RESULT_SERIALIZER = 'json'
# 时区
CELERY_TIMEZONE = 'Asia/Shanghai'
四、创建异步任务
按照 Celery 的约定,任务函数应该放在各 app 下的 tasks.py 文件中。Celery 的 autodiscover_tasks 会自动扫描。
4.1 发送邮件任务
在 apps/users/tasks.py 中创建(如果还没有该文件就新建):
bash
from celery import shared_task
from django.core.mail import send_mail
from django.conf import settings
@shared_task(bind=True, max_retries=3, default_retry_delay=60)
def send_activation_email_task(self, user_id, activation_url, recipient_email):
"""
异步发送激活邮件
"""
try:
send_mail(
subject='激活你的电商账号',
message=f'点击链接激活账号:{activation_url}',
from_email='noreply@example.com',
recipient_list=[recipient_email],
)
except Exception as e:
# 发送失败,自动重试
raise self.retry(exc=e)
@shared_task
def send_order_notification_email_task(user_email, order_no, total_amount):
"""
下单成功后异步发送通知邮件
"""
send_mail(
subject=f'订单 {order_no} 已生成',
message=f'您的订单 {order_no}(金额:¥{total_amount})已生成,请尽快支付。',
from_email='noreply@example.com',
recipient_list=[user_email],
)
参数说明:
-
bind=True:绑定任务实例到self,用于调用self.retry()实现失败重试。 -
max_retries=3:最多重试 3 次。 -
default_retry_delay=60:每次重试间隔 60 秒。
4.2 发送短信任务
在 apps/users/tasks.py 中继续添加:
bash
import logging
logger = logging.getLogger('users')
@shared_task
def send_sms_task(phone, code):
"""
异步发送短信验证码(模拟)
生产环境对接阿里云/腾讯云短信服务
"""
# 模拟发送,实际换成 API 调用
logger.info(f'[异步短信] 向 {phone} 发送验证码:{code}')
# 这里可以调用真实的短信 SDK
# 例如:send_sms(phone, code)
return f'SMS sent to {phone}'
五、改造视图,改为异步调用
现在我们要把之前视图里同步发送邮件/短信的地方,全部替换为调用 Celery 任务。
5.1 注册视图------异步发送激活邮件和短信
编辑 apps/users/views.py,找到 register 函数,修改邮件发送部分:
bash
from .tasks import send_activation_email_task, send_sms_task
def register(request):
# ... 表单验证和用户创建逻辑不变 ...
if email:
# 原来:send_mail(...)
# 现在:调用异步任务
token = str(random.randint(100000, 999999))
request.session[f'email_token_{user.id}'] = token
activate_url = request.build_absolute_uri(
f'/users/activate/{user.id}/{token}/'
)
send_activation_email_task.delay(user.id, activate_url, email)
messages.success(request, '注册成功!激活邮件已发送,请前往邮箱查收。')
之前发送短信验证码的视图 send_sms_code,也可以改为异步(但注意验证码需要存储到 session,我们保留同步写 session,只把"发送"动作异步):
bash
@require_POST
def send_sms_code(request):
phone = request.POST.get('phone')
# ... 生成验证码逻辑 ...
request.session['sms_code'] = code
request.session.set_expiry(300)
# 异步发送短信
send_sms_task.delay(phone, code)
return JsonResponse({'ok': True, 'msg': '验证码已发送(异步)'})
5.2 下单视图------异步发送通知邮件
编辑 apps/orders/views.py,找到 order_submit 视图,在订单创建成功后添加通知邮件任务:
bash
from users.tasks import send_order_notification_email_task
# 在 order_submit 中的订单创建成功后:
send_order_notification_email_task.delay(
user.email, order.order_no, float(order.total_amount)
)
如果用户没有邮箱,可以先判断:
bash
if user.email:
send_order_notification_email_task.delay(
user.email, order.order_no, float(order.total_amount)
)
5.3 确保邮件配置
当前开发环境仍使用控制台后端,我们可以在 settings.py 中临时添加一个真实的 SMTP 配置来测试异步效果(比如使用 QQ 邮箱、163 邮箱的 SMTP)。如果不想配真邮箱,控制台后端依然有效,只是你会看到 Worker 的控制台打印邮件内容,而不再是 Django 主进程打印。
六、启动 Celery Worker 并测试
6.1 启动 Redis(如果尚未启动)
确保 Redis 服务运行中。
6.2 启动 Celery Worker
在项目根目录,打开一个 新的终端窗口,激活虚拟环境后执行:
bash
celery -A django_ecommerce worker -l info
控制台输出示例:
bash
-------------- celery@MacBook-Pro.local v5.3.x
--- ***** -----
-- ******* ---- Linux-6.1.0 x86_64
- *** --- * ---
- ** ---------- [config]
- ** ---------- .> app: django_ecommerce:0x...
- ** ---------- .> transport: redis://127.0.0.1:6379/0
- ** ---------- .> results: redis://127.0.0.1:6379/1
- *** --- * --- .> concurrency: 8 (prefork)
-- ******* ---- .> task events: OFF
--- ***** -----
-------------- [queues]
.> celery exchange=celery(direct) key=celery
[tasks]
. users.tasks.send_activation_email_task
. users.tasks.send_sms_task
. users.tasks.send_order_notification_email_task
看到了三个任务已经被 Worker 发现并注册。
6.3 测试注册激活邮件
启动 Django 开发服务器(在另一个终端):
bash
python manage.py runserver
-
访问注册页,填写邮箱
test@example.com并提交。 -
Django 主进程终端显示请求处理,但 不再打印邮件内容。
-
观察 Celery Worker 终端,会看到任务执行日志:
bash
[2026-05-26 10:15:30,123: INFO/ForkPoolWorker-1] users.tasks.send_activation_email_task[abc123]: 邮件已发送
...
MIME-Version: 1.0
Subject: 激活你的电商账号
...
对比 :之前同步发送时,runserver 终端会打印邮件;现在异步后,邮件发送转移到了 Worker 终端,Django 主线程秒级返回。
6.4 测试短信验证码
点击"获取验证码",Django 终端显示请求,Worker 终端显示:
bash
[2026-05-26 10:18:22,456: INFO/ForkPoolWorker-1] [异步短信] 向 13800138000 发送验证码:384729
6.5 测试下单通知邮件
完成一笔下单流程(购物车 → 确认订单 → 提交订单)。订单创建成功后,Worker 终端输出订单通知邮件的发送内容。
七、常见问题与排错
7.1 Celery Worker 启动报错 ModuleNotFoundError
确保你的项目路径在 Python 搜索路径中,并且启动命令在项目根目录(有 manage.py 的那一级)执行。如果 apps 模块找不到,检查 sys.path.insert 是否在 settings.py 中正确配置。
7.2 任务未被 Worker 执行
检查 celery.py 中 app.autodiscover_tasks() 是否调用,且 tasks.py 文件名是否正确(注意复数 tasks)。
7.3 Redis 连接失败
检查 Redis 是否启动:redis-cli ping。如果 Redis 设置了密码,CELERY_BROKER_URL 格式为:redis://:password@127.0.0.1:6379/0。
八、总结与下集预告
今天我们为电商项目注入了异步能力:
-
安装配置了 Redis + Celery;
-
创建了异步发送邮件和短信的任务,支持失败重试;
-
改造了注册、短信、下单视图,将耗时操作异步化;
-
成功启动了 Celery Worker,完成了异步任务的端到端测试。
现在,用户操作的响应速度大幅提升,系统吞吐量也更高。下一步,我们将利用 Redis 的另一个强大功能------缓存优化 。第 24 篇,我会带你为商品列表、详情页加上 Redis 缓存,让频繁访问的商品数据不再每次都查数据库,性能再上新台阶。
想了解也可以去其它平台搜索「IT策士」,一起升级 IT 思维 !
本文为《Django 从 0 到 1 打造完整电商平台》系列第 23 篇。