消息推送系统开发实战:短信、邮件、微信和APP推送的统一接口设计
消息推送系统开发实战:短信、邮件、微信和APP推送的统一接口设计
用户注册要发验证码、订单状态变了要通知用户、营销活动要群发优惠信息、系统告警要通知运维人员……消息推送是几乎所有业务系统都需要的基础能力。然而在很多企业中,短信发送的代码散落在各个业务模块里,邮件发送用的是另一套逻辑,微信推送又是单独对接的——不仅维护成本高,用户体验也不一致。
构建一个统一的消息推送系统,将多渠道推送能力封装为标准接口,是解决这些问题的根本方案。本文将从架构设计到各渠道对接的技术细节,完整讲解如何开发一个企业级的消息推送系统。
一、为什么需要统一消息推送系统
1.1 散落式推送的问题
在没有统一推送系统的情况下,每个业务模块自行对接短信供应商、邮件服务器、微信API,带来以下问题:
供应商耦合:短信供应商的SDK直接嵌入业务代码中。更换供应商时,需要找到所有调用点逐一修改,遗漏一个就会出问题。
发送逻辑重复:每个模块都自己实现了频率限制、失败重试、日志记录等逻辑,代码重复度极高。
无法全局管控:发送了多少条短信?哪些用户收到了哪些通知?用户投诉收到骚扰短信时如何追溯?分散在各模块的发送记录很难统一查询。
渠道策略无法统一:某个通知到底是发短信还是发微信?用户更希望通过哪个渠道接收?这些决策散落在各业务模块中,无法统一配置和调整。
1.2 统一推送系统的价值
标准接口:业务方只需要调用一个接口——"给用户X发送Y类型的消息",推送系统根据消息类型、用户偏好和渠道策略,自动选择合适的渠道发送。
供应商解耦:渠道供应商的对接逻辑封装在推送系统内部,更换供应商不影响任何业务代码。
统一管控:所有消息的发送记录集中存储,可以全局统计发送量、到达率、用户反馈等指标。
用户体验一致:消息模板、发送频率、退订机制在一处配置,确保用户在各渠道收到的消息风格一致、频率合理。
二、系统架构设计
2.1 整体架构
消息推送系统的架构分为四层:
接入层:提供标准API(REST/gRPC)接收业务方的推送请求。请求包含:接收者(用户ID或手机号/邮箱)、消息类型(验证码/通知/营销)、模板ID、模板参数、指定渠道(可选)。
决策层:根据消息类型和用户偏好,决定通过哪个渠道发送。决策规则包括:用户是否关注了公众号(有则优先微信,省短信费)、用户的免打扰时段、用户的渠道偏好设置、消息类型与渠道的默认映射。
发送层:对接各渠道供应商的API,执行实际的消息发送。每个渠道有独立的发送器(Sender),封装了供应商SDK、失败重试、速率控制等逻辑。
追踪层:记录每条消息的发送状态、送达回执、用户阅读状态,提供查询和统计接口。
2.2 消息流转流程
一条消息从接收到送达的完整流程:
- 业务方调用API发送推送请求
- 接入层校验参数、鉴权、生成消息ID
- 消息写入消息队列(如RabbitMQ/Kafka)
- 决策层从队列消费消息,渲染模板、选择渠道
- 消息投递到对应渠道的发送队列
- 发送层从渠道队列消费,调用供应商API发送
- 收到发送结果(成功/失败),更新状态
- 如果失败,根据重试策略重新投递到发送队列
- 收到送达回执(短信的状态报告、邮件的已读回执),更新状态
为什么要用消息队列:推送请求可能有突发流量(如促销活动开始瞬间群发百万条短信),消息队列起到削峰填谷的作用,保护下游渠道API不被打垮。同时消息队列保证了消息不丢失——即使发送器临时宕机,消息仍然在队列中等待处理。
2.3 高可用设计
发送器多实例:每个渠道的发送器部署多个实例,从同一个队列消费。某个实例宕机时,消息被其他实例消费,不影响发送。
供应商主备:短信渠道配置多个供应商(如主用阿里云短信,备用腾讯云短信),主供应商发送失败时自动切换到备用供应商。
降级策略:微信推送失败时降级为短信推送,短信推送失败时降级为邮件推送(如果场景允许)。不是所有消息都适合降级——验证码必须通过短信,不能降级到邮件。
三、消息模板管理
3.1 模板系统设计
消息内容不应该在业务代码中硬编码,而是通过模板管理。模板将消息的结构和内容分离,运营人员可以在后台修改消息文案而不需要开发介入。
模板的核心字段:
- 模板ID:全局唯一标识
- 模板名称:用于管理界面展示(如"订单发货通知")
- 消息类型:验证码/事务通知/营销推广
- 各渠道内容:同一个模板在不同渠道有不同的内容格式
短信模板示例:【万户科技】您的订单${orderNo}已发货,快递公司${expressName},快递单号${expressNo},请注意查收。
邮件模板示例:HTML格式的邮件正文,包含品牌logo、格式化的订单信息表格、操作按钮。
微信模板消息:使用微信公众平台的模板消息接口,模板ID由微信分配,参数通过keyword字段传入。
3.2 模板变量渲染
模板中的变量(如${orderNo})在发送时替换为实际值。渲染引擎需要支持:
简单变量替换:${name} → 实际值
条件判断:#if(status=='已发货') ... #else ... #end。根据业务状态展示不同的文案。
列表循环:订单通知中包含多个商品时,需要循环渲染商品列表。
过滤器:对变量值做格式化处理,如日期格式化${createTime|dateFormat:'yyyy-MM-dd'}、金额格式化${amount|currency}。
渲染引擎可以使用Freemarker、Velocity(Java生态)或Jinja2(Python生态),这些模板引擎都支持上述功能。
3.3 模板审核
营销类短信的模板需要经过运营商审核才能使用。系统需要管理模板的生命周期:
创建 → 提交审核 → 审核中 → 审核通过/审核拒绝 → 上线/修改后重新提交
不同短信供应商的模板审核是独立的,同一个模板需要分别在阿里云、腾讯云等平台提交审核。系统记录每个供应商的审核状态和模板ID映射。
四、各渠道对接技术细节
4.1 短信渠道
供应商对接:主流短信供应商(阿里云短信、腾讯云短信、云片网等)都提供HTTP API。发送参数通常包括:手机号、模板ID(供应商端的)、模板参数、签名。
签名管理:短信签名(如【万户科技】)需要在供应商平台备案审核。不同业务可能用不同签名——客服通知用公司名,某个产品的通知用产品名。
发送频率限制:
- 同一手机号同一模板每分钟不超过1条(防止接口被刷发验证码)
- 同一手机号每天不超过10条(防止骚扰)
- 同一手机号每小时不超过5条(防止集中轰炸)
频率限制使用Redis的滑动窗口计数器实现。Key为sms:limit:{phone}:{templateId}:{timeWindow},每次发送前检查计数,超过限制则拒绝发送。
状态报告:短信供应商会异步回调发送状态(送达成功/发送失败/手机关机等)。系统需要提供回调接口,收到状态报告后更新消息状态。不同供应商的回调格式不同,需要在各自的适配器中做解析。
敏感操作:验证码类短信需要特殊处理——生成的验证码存在Redis中设置过期时间(如5分钟),验证时比对后立即删除(一次性使用),同一验证码不能跨场景使用。
4.2 邮件渠道
发送方式:通过SMTP协议发送,或使用邮件服务商的API(如SendGrid、Mailgun、阿里云邮件推送)。API方式比SMTP更可靠、支持更好的送达追踪。
HTML邮件开发:邮件的HTML渲染引擎跟浏览器差异很大,很多CSS属性不支持。邮件HTML的编写原则:
- 使用table布局而非div+CSS布局(Outlook等客户端不支持flex/grid)
- CSS使用inline style而非外部样式表
- 图片使用绝对URL,不要用base64内嵌(很多客户端会拦截)
- 宽度不超过600px(适配各种邮件客户端)
- 提供纯文本版本(multipart/alternative),部分客户端默认显示纯文本
邮件到达率优化:
- 配置SPF、DKIM、DMARC记录,证明邮件来源合法
- 控制发送频率,避免被反垃圾邮件系统标记
- 维护退订列表,用户点击退订后不再发送
- 避免使用垃圾邮件常见的关键词(免费、赢取、限时等)
- 定期清理无效邮箱(硬退信的地址)
追踪机制:在邮件中嵌入1x1的透明追踪像素(一个图片标签,src指向推送系统的打开追踪接口),用户打开邮件时加载图片,系统记录打开事件。链接追踪通过替换邮件中的链接为推送系统的重定向链接实现。
4.3 微信渠道
模板消息:微信公众号的模板消息是最常用的推送方式。限制条件:模板必须从微信公众平台的模板库中选择或申请、每个用户每天接收的模板消息有数量限制、模板消息只能发给已关注公众号的用户。
发送流程:获取用户的openid → 调用微信的模板消息发送API → 传入模板ID和参数。需要维护用户ID与openid的映射关系。
小程序订阅消息:微信小程序使用订阅消息替代了模板消息,需要用户主动订阅才能发送。每次订阅只允许发送一条消息(一次性订阅)或在用户选择后长期接收(长期订阅)。
企业微信消息:内部系统通知可以通过企业微信的消息接口发送到员工的企业微信。支持文本、图片、图文、文件等多种消息类型,且没有数量限制。
4.4 APP推送
推送通道:
| 平台 | 通道 | 特点 |
|---|---|---|
| iOS | APNs(Apple Push Notification) | 统一通道,稳定可靠 |
| Android(海外) | FCM(Firebase Cloud Messaging) | Google统一通道 |
| Android(国内) | 各厂商通道(华为/小米/OPPO/vivo) | 国内无Google服务,需分别对接 |
国内Android推送的痛点:没有统一的推送通道,APP被杀后就收不到消息。解决方案是对接各手机厂商的系统级推送通道——华为Push、小米Push、OPPO Push、vivo Push。通过设备信息判断手机品牌,走对应的厂商通道。
推送SDK整合:为了简化多厂商对接的工作,可以使用第三方推送服务(如极光推送、个推、友盟推送),它们封装了所有厂商通道的对接。
通知栏样式:支持自定义通知图标、标题、正文、大图、操作按钮。不同Android版本对通知栏样式的支持不同,需要做兼容处理。
静默推送:不在通知栏展示,直接将数据传递给APP内部处理。用于数据同步、缓存更新等不需要用户感知的场景。
五、发送策略与用户偏好
5.1 渠道选择策略
同一条业务消息,应该通过哪个渠道发送?决策逻辑:
-
强制渠道:验证码必须走短信,密码重置链接必须走邮件——这些由消息类型决定,不受用户偏好影响。
-
用户偏好:用户在设置中选择了"通过微信接收通知",则优先使用微信。
-
渠道可达性:用户没有绑定微信,即使偏好微信也只能走短信。
-
成本优先:微信模板消息免费,短信按条计费。在效果相当的前提下优先使用成本更低的渠道。
-
降级策略:微信推送失败后降级到短信,确保消息最终送达。
5.2 免打扰设置
全局免打扰:用户设置22:00-08:00为免打扰时段,期间不发送任何非紧急消息。紧急消息(如安全告警)不受限制。
分类免打扰:用户可以针对不同类型的消息设置是否接收。例如关闭营销消息但保留订单通知。
频率保护:即使业务方没有做频率限制,推送系统也要有全局的保护策略——同一用户每天收到的总消息数不超过N条(紧急消息除外)。超过限制的消息可以合并为一条摘要消息,或者延迟到第二天发送。
5.3 智能发送时间
营销类消息的发送时间对打开率有很大影响。分析用户的历史行为数据(什么时间段打开率最高),在最佳时间发送。
简单做法:按时段统计打开率,选择整体打开率最高的时段发送(如下午14:00-16:00)。
高级做法:为每个用户建立个体化的最佳发送时间模型,个性化发送。这需要足够的数据量支撑。
六、推送效果追踪与分析
6.1 追踪指标
| 指标 | 定义 | 重要性 |
|---|---|---|
| 发送量 | 成功调用发送API的消息数 | 基础 |
| 到达率 | 消息成功送达设备/邮箱的比例 | 核心 |
| 打开率 | 用户打开/阅读消息的比例 | 核心 |
| 点击率 | 消息中链接的点击比例 | 营销类重要 |
| 退订率 | 用户点击退订的比例 | 营销类重要 |
| 转化率 | 推送带来的目标行为(下单/注册等)比例 | 营销类重要 |
6.2 数据收集
短信:到达率通过供应商的状态报告获取。打开率和点击率短信无法直接追踪,只能通过短链接的点击数间接衡量。
邮件:到达率通过退信率反推(发送量-退信量=到达量)。打开率通过追踪像素统计。点击率通过链接追踪统计。
微信:微信API会返回发送结果(成功/用户取消关注/接口调用超时等)。模板消息的打开追踪需要在详情页埋点。
APP推送:厂商通道会返回消息ID,可以追踪到达状态。打开追踪通过APP端上报——用户点击通知后,APP向推送系统上报打开事件。
6.3 分析报表
渠道对比报表:对比各渠道的到达率、打开率、成本,辅助渠道策略调整。
模板效果排行:哪些消息模板的打开率高、哪些模板导致大量退订,指导文案优化。
时段分析:不同时段的推送效果对比,优化发送时间策略。
A/B测试:对同一批用户随机分组,发送不同版本的消息内容,对比效果选择最优版本。推送系统需要支持按比例分流——10%用户收到A版本、10%收到B版本、80%收到默认版本。
七、开发实施建议
7.1 技术选型
消息队列:Kafka适合大规模高吞吐场景(日发送百万级以上),RabbitMQ适合中小规模且需要灵活路由的场景。
存储:消息记录量大(每天可能上百万条),用MySQL存储近期数据(30天),历史数据归档到ClickHouse或Elasticsearch中。
缓存:Redis用于频率限制、验证码存储、用户偏好缓存。
7.2 接口设计
对外API保持简洁,核心接口只有两个:
单条推送:POST /api/message/send
{
"userId": "10086",
"templateId": "ORDER_SHIPPED",
"params": {"orderNo": "WO202610220001", "expressName": "顺丰速运"},
"channel": null // null=系统自动选择
}
批量推送:POST /api/message/batch
{
"userIds": ["10086", "10087", "10088"],
"templateId": "PROMOTION_DOUBLE11",
"params": {"discount": "满300减50"}
}
广州万户网络在消息推送系统的开发和集成方面有丰富经验,无论是独立的消息中台还是集成到现有业务系统中的推送模块,都能提供成熟的技术方案。
7.3 运维注意事项
监控短信余额:短信按条计费,需要监控各供应商的账户余额,余额低于阈值时自动告警。避免因为余额不足导致短信发送失败。
黑名单管理:用户退订后加入黑名单,后续推送请求自动过滤。同时维护全局投诉黑名单——被运营商标记为投诉号码的用户,停止所有营销类推送。
灰度发布:新模板或新的发送策略上线前,先对小比例用户进行灰度验证,确认无问题后再全量发布。避免模板变量渲染错误导致大量用户收到"${name}您好"这样的乌龙消息。
容灾演练:定期模拟主供应商宕机的场景,验证自动切换到备用供应商的机制是否正常工作。真正出问题时再发现切换逻辑有bug,就来不及了。
总结
统一消息推送系统的核心价值在于将分散在各业务模块中的推送逻辑收拢为标准化的基础服务,让业务方只关心"发什么消息给谁",而不需要关心"怎么发、通过哪个渠道发、失败了怎么办"。
系统开发的重点在于:多渠道的统一封装与灵活切换、消息模板的管理与渲染、发送策略和用户偏好的智能匹配、以及完整的推送效果追踪链路。从简单的短信+邮件开始,逐步扩展到微信和APP推送,在实践中不断优化发送策略和用户体验。
需要网站建设、软件开发或爬虫定制?
模板建站1280元起,价格公开不加价。电话/微信 13535321113
