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:
brewinstallredis brew services start redisUbuntu/Debian:
sudoaptupdatesudoaptinstallredis-serversudosystemctl start redisWindows:
从 Redis for Windows 下载 msi 安装包,安装后会自动启动服务。
验证 Redis 是否启动:
控制台输出:
如果返回PONG,说明 Redis 正常运行。
2.2 安装 Celery
进入项目虚拟环境:
pipinstallcelery[redis]这条命令会同时安装 Celery 和它所需的 Redis 客户端依赖。
控制台输出:
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:
importos from celeryimportCelery# 设置 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:
from .celeryimportapp as celery_app __all__=('celery_app',)3.3 在 settings.py 中增加 Celery 配置
在django_ecommerce/settings.py末尾添加:
# ==================== 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中创建(如果还没有该文件就新建):
from celeryimportshared_task from django.core.mailimportsend_mail from django.confimportsettings @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中继续添加:
importlogging logger=logging.getLogger('users')@shared_task def send_sms_task(phone, code):""" 异步发送短信验证码(模拟) 生产环境对接阿里云/腾讯云短信服务"""# 模拟发送,实际换成 API 调用logger.info(f'[异步短信] 向 {phone} 发送验证码:{code}')# 这里可以调用真实的短信 SDK# 例如:send_sms(phone, code)returnf'SMS sent to {phone}'五、改造视图,改为异步调用
现在我们要把之前视图里同步发送邮件/短信的地方,全部替换为调用 Celery 任务。
5.1 注册视图——异步发送激活邮件和短信
编辑apps/users/views.py,找到register函数,修改邮件发送部分:
from .tasksimportsend_activation_email_task, send_sms_task def register(request):# ... 表单验证和用户创建逻辑不变 ...ifemail:# 原来: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,只把“发送”动作异步):
@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)returnJsonResponse({'ok':True,'msg':'验证码已发送(异步)'})5.2 下单视图——异步发送通知邮件
编辑apps/orders/views.py,找到order_submit视图,在订单创建成功后添加通知邮件任务:
from users.tasksimportsend_order_notification_email_task# 在 order_submit 中的订单创建成功后:send_order_notification_email_task.delay(user.email, order.order_no, float(order.total_amount))如果用户没有邮箱,可以先判断:
ifuser.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
在项目根目录,打开一个新的终端窗口,激活虚拟环境后执行:
celery-Adjango_ecommerce worker-linfo控制台输出示例:
-------------- 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].>celeryexchange=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 开发服务器(在另一个终端):
python manage.py runserver访问注册页,填写邮箱
test@example.com并提交。Django 主进程终端显示请求处理,但不再打印邮件内容。
观察 Celery Worker 终端,会看到任务执行日志:
[2026-05-2610:15:30,123: INFO/ForkPoolWorker-1]users.tasks.send_activation_email_task[abc123]: 邮件已发送... MIME-Version:1.0Subject: 激活你的电商账号...对比:之前同步发送时,runserver终端会打印邮件;现在异步后,邮件发送转移到了 Worker 终端,Django 主线程秒级返回。
6.4 测试短信验证码
点击“获取验证码”,Django 终端显示请求,Worker 终端显示:
[2026-05-2610:18:22,456: INFO/ForkPoolWorker-1][异步短信]向13800138000发送验证码:3847296.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 篇。
