促销活动 Push 通知链路优化:从初期瓶颈到千万级支撑的演进

133 阅读7分钟

在促销活动场景中,运营配置活动后需向全量用户发送 Push 通知,随着用户量从万级增长到千万级、业务复杂度提升,Push 链路经历了三次关键优化。本文将拆解各阶段的设计、瓶颈与改进方案,复盘从 “简单循环” 到 “高并发支撑” 的演进逻辑。

一、阶段一:初期方案 —— 滚动拉取 + 直接调用,痛点明显

初期用户量较小,Push 链路采用 “滚动拉取用户数据 + 线程池 /for 循环调用推送接口” 的模式,流程简单:拉取用户列表后,逐个组装推送模板,直接调用 SDK 发送。但随着用户量增长,三大类问题逐渐暴露。

image.png

1.1 性能效率瓶颈:资源占用高,吞吐量上不去

  • for 循环的 “串行陷阱” :若用 for 循环处理用户,需依次调用推送接口,当用户量达 10 万级时,整体耗时从分钟级变成小时级,系统长时间占用 CPU 与网络资源,拖慢其他业务(如订单处理、商品查询);
  • 线程池的 “配置困境” :线程数设置过少,高并发时任务积压;设置过多,又会引发线程竞争,反而降低吞吐量。且缺乏合理的任务队列与拒绝策略,高峰期易出现 “任务丢失” 或 “系统卡顿”。

1.2 稳定性风险:异常扩散,无容灾能力

  • 单个故障影响全局:滚动拉取或推送中,若某一个用户处理出错(如模板组装失败、SDK 调用超时),未做隔离处理会导致整个循环中断,或影响后续用户的推送,推送成功率从 99% 骤降到 80% 以下;
  • 缺乏重试机制:遇到网络抖动、SDK 临时不可用等问题,失败的推送任务无法重试,直接丢失 —— 比如某次促销活动因网络波动,近 10% 用户未收到通知,影响活动转化。

1.3 业务扩展性差:耦合高,难适配复杂场景

  • 代码耦合严重:用户拉取、模板组装、SDK 调用逻辑混在一起,若要更换推送 SDK(如从极光换成个推)或修改模板格式,需改动整个链路的代码,维护成本高,还容易引入新 bug;
  • 复杂场景难支持:当业务需要 “按用户分层推送”(如 VIP 用户优先推送)、“分时段推送”(避开用户休息时间)时,简单的循环逻辑无法扩展,需大改流程才能实现。

二、阶段二:引入 MQ + 模块划分,解耦但仍有瓶颈

为解决阶段一的痛点,我们引入 MQ(消息队列)实现服务解耦,并按职责拆分模块,链路性能与稳定性有了明显提升,但面对百万级用户仍显不足。

image.png

2.1 核心改进:MQ 解耦 + 模块化设计

  • MQ 实现异步与并发:引入 MQ 集群,运营服务创建活动后,将 “推送任务” 发送到 MQ;推送服务部署多消费节点,并发消费 MQ 消息,相比阶段一的串行处理,吞吐量提升 3-5 倍,整体推送耗时缩短 60%;
  • 服务职责拆分:运营服务专注于 “活动配置 + 用户数据查询”,推送服务专注于 “模板组装 + SDK 调用”,资源可针对性配置(如给推送服务分配更多 CPU),避免功能耦合导致的资源争抢;
  • 配置化与可插拔:支持活动配置(如推送时间、目标用户群)、模板配置(动态选择文案与渠道),同时用抽象工厂模式封装推送组件 —— 新增推送渠道(如短信、APP 推送)时,只需扩展工厂类,无需修改核心逻辑。

2.2 未解决的瓶颈:千万级用户下的效率与可靠性问题

  • 缺乏 Batch 处理与精细拆分:虽用多节点并发,但用户数据仍按 “单条” 处理,网络请求次数多(如 100 万用户需 100 万次 SDK 调用),资源开销大;且未按用户 ID 区间拆分任务,单节点处理大批次数据时仍会积压;
  • 幂等性缺失:MQ 消息重试、消费异常时,可能导致同一用户重复接收 Push—— 某次活动因 MQ 重试,5% 用户收到 2 次通知,引发投诉;
  • 并行度不足:未充分利用多节点与多线程的组合优势,单节点仅用 10-15 个线程处理任务,面对千万级用户,推送耗时仍需数小时,无法满足 “活动上线后 1 小时内触达全量用户” 的需求。

三、最终版:任务拆分 + Batch 处理 + 幂等保障,支撑千万级用户

针对阶段二的瓶颈,我们以 “40 万用户推送” 为试点,优化任务拆分逻辑、引入 Batch 处理与幂等控制,最终实现 “分钟级推送”,可支撑千万级用户体量。

image.png

3.1 核心优化:效率、可靠性、精细化三管齐下

(1)任务拆分 + Batch 处理,大幅提升效率

  • 分层拆分任务:先按 “用户 ID 区间” 拆分大任务(如 40 万用户拆成 400 个 “1000 用户 / 任务”),再将每个任务拆成 10 个 “100 用户 /list”;
  • Batch 减少请求:推送服务用 “MQ Batch 消息 + SDK Batch 调用”,100 条用户数据只需 1 次 SDK 调用 —— 对比阶段二的单条处理,1000 条数据的推送耗时从 10 秒降到 100 毫秒,效率提升 100 倍,网络请求次数减少 99%。

(2)多节点 + 多线程,并行度拉满

  • 多节点并行提取用户:用户提取服务部署 5-8 个节点,每个节点用 30 个线程(4C8G 机器配置)处理任务,40 万用户的用户数据提取耗时从 30 分钟降到 5 分钟;
  • 推送服务并行处理:推送服务同样用多节点 + 多线程,结合 Batch 调用,40 万用户的推送耗时从 2 小时降到 15 分钟,千万级用户可控制在 1 小时内。

(3)Redis 保障幂等,避免重复推送

  • 推送状态记录:推送前先查 Redis(key 为 “活动 ID + 用户 ID”,value 为 “是否推送成功”),未推送则执行后续流程;
  • 推送后更新状态:SDK 调用成功后,立即更新 Redis 状态为 “已推送”,即使 MQ 重试,也不会重复处理 —— 重复推送率从 5% 降到 0.1% 以下,用户体验与系统无效开销均大幅改善。

(4)流程精细化,适配高并发

  • 全链路流程优化:活动创建→MQ 发送任务→活动消费者拆分 ID 区间→用户提取消费者 Batch 拉取数据→推送服务消费者 Batch 调用 SDK,每个环节明确职责,可独立扩容;
  • 资源按需分配:用户提取服务侧重数据库查询,分配更多内存;推送服务侧重网络 IO,分配更多 CPU 与线程,资源利用率提升 40%。

3.2 最终成效:满足业务时效与体量要求

40 万用户推送耗时从阶段二的 2 小时降到 15 分钟,千万级用户可支撑 “1 小时内全量触达”;推送成功率稳定在 99.9% 以上,重复推送率低于 0.1%,完全满足当时项目的用户体量(千万级)与业务时效要求(活动上线后 1 小时内触达)。

总结:Push 链路优化的核心逻辑

从初期到最终版,优化的核心始终围绕 “三个目标”:

  1. 效率提升:通过 “异步化(MQ)→ Batch 处理→ 多节点多线程”,持续降低耗时与资源开销;
  1. 可靠性保障:从 “无重试” 到 “MQ 重试”,再到 “Redis 幂等控制”,逐步解决故障恢复与重复问题;
  1. 扩展性增强:从 “耦合代码” 到 “模块化”,再到 “精细化流程”,让链路能适配用户量与业务场景的变化。

这套优化思路不仅适用于 Push 通知,也可迁移到 “全量用户短信发送”“大规模数据同步” 等类似场景 —— 核心是 “按需拆分任务、合理利用资源、提前规避风险”。