Django 从 0 到 1 打造完整电商平台:使用 Celery 异步发送邮件/短信

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
  1. 访问注册页,填写邮箱 test@example.com 并提交。

  2. Django 主进程终端显示请求处理,但 不再打印邮件内容

  3. 观察 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.pyapp.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 篇。

相关推荐
武子康几秒前
Java-07 深入浅出 MyBatis数据库一对多关系模型实战:表结构设计与查询实现
java·后端
花椒技术44 分钟前
企业内部 Agent 落地复盘:Gateway、Skill 和二次确认如何串起受控业务执行
后端·agent·ai编程
PAK向日葵2 小时前
我用 C++ 写了一个轻量级 Python 虚拟机,刚刚开源
c++·python·开源
我是一颗柠檬3 小时前
【MySQL全面教学】MySQL事务与ACID Day9(2026年)
数据库·后端·mysql
枕星而眠3 小时前
数据结构八大排序详解(一):四大简单排序
c语言·数据结构·c++·后端
IT_陈寒3 小时前
React useEffect闭包陷阱差点把我整失业了
前端·人工智能·后端
苍何3 小时前
爆肝两周,我把 Codex 最全实战指南开源了
后端
财经资讯数据_灵砚智能3 小时前
基于全球经济类多源新闻的NLP情感分析与数据可视化(日间)2026年5月26日
大数据·人工智能·python·信息可视化·自然语言处理·ai编程·灵砚智能
bug菌4 小时前
【SpringBoot 3.x 第254节】夯爆了,数据库访问性能优化实战详解!
数据库·spring boot·后端
我材不敲代码4 小时前
Python基础:列表详解、增删改查及常用高阶操作
开发语言·windows·python