11.3 异步任务与信号


文档摘要

11.3 异步任务与信号 本节摘要:有一类慢是"必须做但不能让用户等":发通知、处理图片、生成报表。Celery 把这些活搬进后台队列,请求立刻返回。信号则是 Django 内置的发布订阅机制,让模块在"某事发生"时被通知而不互相引用。队列解决"慢",信号解决"缠",本节各讲清楚,并划出两者的边界。 队列:把慢活搬出请求 墨迹博客的痛点:作者上传封面图后,保存按钮转圈五秒——同步做图片裁切,用户干等。异步化三部曲:任务函数、消息代理、worker 进程。 视图里改为"派活不等活": 架构上的角色:生产者(Django 视图)把任务消息放进代理(Redis,复用第 10 章拓扑里那台),消费者(Celery worker 独立进程)取消息执行。

11.3 异步任务与信号

本节摘要:有一类慢是"必须做但不能让用户等":发通知、处理图片、生成报表。Celery 把这些活搬进后台队列,请求立刻返回。信号则是 Django 内置的发布订阅机制,让模块在"某事发生"时被通知而不互相引用。队列解决"慢",信号解决"缠",本节各讲清楚,并划出两者的边界。

队列:把慢活搬出请求

墨迹博客的痛点:作者上传封面图后,保存按钮转圈五秒——同步做图片裁切,用户干等。异步化三部曲:任务函数、消息代理、worker 进程。

# 任务定义:与普通函数几乎一样,加装饰器 from celery import shared_task @shared_task def process_cover(article_id): article = Article.objects.get(pk=article_id) # 生成多尺寸缩略图、压缩、上传存储 make_thumbnails(article.cover) article.thumbnail_ready = True article.save()

视图里改为"派活不等活":

form.instance.author = request.user article = form.save() process_cover.delay(article.id) # 进队列 立即返回 return redirect(...)

架构上的角色:生产者(Django 视图)把任务消息放进代理(Redis,复用第 10 章拓扑里那台),消费者(Celery worker 独立进程)取消息执行。用户端的保存动作从五秒变几十毫秒,图片几分钟内悄悄就绪。

11-03-fig01

实践要点四条:任务传主键不传对象(对象进消息要序列化,出队时数据可能已旧,主键取新最稳);任务要幂等(网络抖动会重试,缩图任务重复执行无碍,转账任务必须幂等设计);失败要有重试与死信观察(装饰器配重试上限与退避,超过进失败队列人工处理);监控队列积压(消费速度长期低于生产速度就该加 worker 或查慢任务)。

定时任务是队列体系的另一半:周期性任务清孤儿文件、凌晨生成统计报表、每周汇总热门文章,配置调度计划即可,把"人肉记得跑脚本"从运维清单里划掉。

信号:内置的发布订阅

第 7 章建 Profile 时已用过信号:用户创建后自动建资料。信号是框架在固定时机广播的事件,订阅方接收而不打扰:

from django.db.models.signals import post_save from django.dispatch import receiver @receiver(post_save, sender=User) def create_profile(sender, instance, created, **kwargs): if created: # 只在新建时 Profile.objects.create(user=instance)

常用信号:模型保存前后、删除前后、请求开始结束。典型用途:保存后清缓存(补上第 11.1 节的失效缺口)、审计日志、资料自动创建。

信号的两个纪律:别用信号写核心业务流——它是"旁路"机制,主流程藏在信号里会让代码流向不可追,跨模块的事件通知才是本职;订阅集中登记(应用配置的 ready 方法里),散落各处的 receiver 是维护噩梦。

队列与信号的边界

两者都算"解耦",方向不同:信号是同步的通知,处理发生在当前请求进程内,适合轻量动作(清个缓存、建条记录);队列是异步的转移,工作被搬到另一进程,适合耗时操作。组合也常见:信号捕获"文章已发布",处理函数只做一件事——往队列派发"群发通知"任务,重活由 worker 干。判据一句话:毫秒级旁路用信号,秒级任务用队列

⚠️ 信号的隐秘坑:测试里直接构造模型实例同样触发信号,测试数据库被意外塞进 Profile 数据。断言时留意信号副作用,必要时在测试里临时断开订阅。

引入队列后还要正视一个运维事实:系统从"一个进程"变成"两套进程",故障面随之变宽。broker 宕机时任务派发会抛异常,视图里对 delay 的调用就要决定降级策略——图片处理派不出去,是让保存失败,还是标记待处理稍后重试?墨迹博客选了后者:封面字段先置空、状态标记处理中,worker 恢复后补跑。这个决定背后是同一个产品判断:异步任务承载的是"锦上添花"的活,它的故障不应拖垮主流程的可用性。上线队列前请把这类降级路径想清楚,否则后台组件的抖动会以"整站不能发文"的形态报复性放大。

队列的容量规划

队列系统还有一个部署时就要想清楚的问题:容量。它包含两个维度——队列能积压多少任务不丢,worker 每分钟能消化多少任务。规划错方向的典型症状有两种:流量高峰时队列长度持续上涨、任务完成时间越来越晚,这是 worker 算力不足;而盲目加 worker 又可能把数据库连接池打满,任务反而更慢,这是算力与下游资源失衡。墨迹博客的实践是给队列长度设告警线(超过五百条任务持续五分钟即通知),并做过一次容量测试:模拟高峰时段的发文频率翻倍,观察队列消化速度,据此把 worker 从两个加到四个并调大了数据库连接上限。容量规划不需要精确,需要的是有意识地留余量并在真实峰值到来前做过演习——队列这类平时隐形、峰值致命的组件,尤其如此。

本节要点回顾

  • 队列三角色:生产者派活、代理暂存、worker 消费,用户不再等慢活。
  • 任务四纪律:传主键、幂等、重试上限、监控积压。
  • 信号是同步旁路,用于事件通知与轻量联动,主流程别藏进去。
  • 毫秒旁路信号、秒级任务队列,组合使用各得其所。

性能与效率的维护体系成形。最后一章做安全加固与全周期复盘,为这段旅程收官。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U