你第一次接触“推送消息到手机”这个需求时,很可能觉得很简单:后端调用一个接口,手机端收到通知,完事。但等真把App装到测试机上,你很快会遇到一系列让人困惑的现象——App刚退到后台就收不到消息了,换了一台安卓手机行为完全不一样,iOS那边永远比你预想得多一道签名流程。更麻烦的是,很多后台推送服务显示“发送成功”,用户手机上却什么都没有。
这篇文章想解释清楚一个核心判断:消息推送的难点不是“把消息发出去”,而是“如何在设备不在线、进程被杀、用户关掉通知权限的情况下,仍然把消息可靠地送到用户眼前”。如果你只是写个接口让App每秒轮询一次,开发环境看着没问题,线上大概率会崩。真正适合长期使用的推送体系,需要先做好技术选型,再决定是自己搭一套通道,还是接入厂商推送SDK。
文章会先讲清楚消息推送的基本原理,然后给出一份选型清单,接着用最小成本搭建一条“服务端 → EMQX → Android手机”的完整推送链路。你可以通过一条命令触发订单状态推送,在手机端实时收到结果。最后,我会把生产环境里那些真正容易踩坑的地方和排查思路列出来。这是一篇偏向实践的文章,适合后端开发、App开发,以及准备做IoT或内部工具通知的开发团队阅读。
1. 这篇文章真正要解决的问题
很多团队接到“推送消息到手机”的需求时,第一反应是扩展现有的HTTP接口:App每隔几秒去服务端查一次状态。这种方案有个形象的称呼叫“轮询”。轮询在最简单的场景下确实能工作,但它有几个隐藏成本。
首先,轮询造成大量无效请求。用户没有新消息时,App仍然要每隔几秒发一次请求,这浪费用户流量,也浪费服务端连接资源。想象一下,如果有1万个在线用户,每个用户每5秒请求一次,服务端每秒要扛2000次请求,而其中绝大多数请求根本拿不到新数据。其次,轮询的实时性永远受“轮询间隔”限制。间隔设短了浪费资源,设长了用户会觉得消息来得太慢。更麻烦的是,当App进程被系统回收后,轮询代码根本没机会执行,消息自然就收不到。
推送和普通请求的核心差别在于:普通请求是“客户端主动去取”,推送是“服务端主动把消息塞给客户端”。要做到后者,客户端必须和推送服务保持一个实时在线的长连接。长连接由谁来维护、进程被杀后还能不能继续工作,这决定了不同推送方案的能力边界。
看完本文你应该能回答下面几个问题:
- 为什么不能靠HTTP轮询顶替推送?
- APNs、厂商通道、聚合Push、自建MQTT分别解决什么问题?
- 从零搭一条自建推送链路需要哪些组件?
- 为什么服务端显示发送成功,手机却收不到通知?
- 生产环境下,自建推送有哪些绕不开的工程细节?
2. 消息推送核心原理:Push与Pull的本质差异
2.1 Push与Pull的区别
Push和Pull描述的是消息流动的方向。Pull是客户端主动询问服务端“有没有新数据”,服务端被请求后才返回结果。Push是服务端一旦产生新消息,就主动通过长连接把数据推给客户端。
日常开发里最常见的Pull就是轮询。它的优点是实现简单,服务端只要提供普通HTTP接口即可,不需要维护额外的长连接服务。缺点是实时性受制于轮询周期,而且服务端无法主动感知客户端是否还活着。Push则反过来,服务端维护与客户端的连接状态,消息一来就往下发。它的实时性更好,省去大量无效请求,但复杂度明显更高,需要额外设计消息通道、连接保活、离线消息等机制。
用生活中的例子来理解:Pull就像你去快递柜取件,快递到了先放在柜子里,你有空就去刷一下柜子看看有没有新包裹;Push则像快递员直接打电话告诉你“包裹到了”。前者需要你反复检查,后者不需要你主动关心,前提是快递员知道你的手机号且你的手机开机。
2.2 系统级长连接与App自建长连接
真正影响推送到达率的关键,是长连接由谁来维护。
系统级长连接是指操作系统自己维护的一条连接。比如iOS的APNs、安卓手机厂商的推送服务,它们作为系统组件常驻,App即使没有被启动,系统收到新消息后也能通过通知栏把内容展示给用户。这个过程对App来说是透明的,你的App进程是否活着通常不影响消息的最终展示。这是市面上主流App能够做到“杀不掉”通知的核心原因。
App自建长连接则是指App自己启动一个网络连接,在后台维持与某个推送服务器的通信。比如接入一个自己的WebSocket服务,或者通过MQTT保持一条TCP长连接。这种连接一旦App进程被杀,连接就断了,消息也就收不到了。除非你的App能够成为系统白名单应用,或者用户手动允许App后台自启动,否则自建通道很难保证高到达率。
这也是判断一个Push方案能不能用于面向大众用户的C端产品时,最核心的一个分水岭。系统级通道做不好,App保住不了,那消息到达率就一定会打折扣。
2.3 MQTT协议为什么适合推送场景
如果把“推送”抽象成“让一个消息从服务端到达指定设备”,那么MQTT是一个非常适合的协议候选。MQTT全称是Message Queuing Telemetry Transport,最初是为低带宽、不可靠网络环境设计的轻量消息协议。它的核心模型是发布/订阅:发送者把消息发布到某个主题,订阅了这个主题的客户端会收到消息。
MQTT不太像传统消息队列,倒更像一个“群聊”:谁在群里订阅了这个主题,谁就能收到消息。服务端不用显式维护“每个设备该发什么”的地址表,设备连上来之后自己订阅即可。一对多、多对多都方便。MQTT的报文头部开销非常小,适合移动网络;它还支持QoS 0、1、2三种投递级别,服务端可以按业务要求选择是否需要确认消息送达。
需要注意的是,不要把MQTT和Kafka混为一谈。Kafka面向大数据场景下的流式吞吐,侧重消息堆积与顺序处理;MQTT面向物联网和移动弱网,侧重设备连接数、低带宽和弱网稳定性。如果只是给几千或几万台设备推送简单状态,MQTT是非常轻量的选择;如果你要做海量日志收集或事件流分析,那应该考虑Kafka这类消息中间件。
2.4 名词澄清:推送消息、通知栏消息、透传消息
不少新手会混淆“推送消息”和“通知栏消息”。在多数推送SDK的语境里,通知栏消息是指系统收到消息后直接展示成一条系统通知;透传消息则是指服务端把一段自定义数据发给App,App决定如何处理,是否弹通知、弹什么样的通知都由App自己控制。
通知栏消息的好处是不需要App进程在前台,系统会展示;坏处是展示样式和点击跳转让渡给了系统或SDK的默认行为。透传消息的好处是灵活,App拿到数据后可以更新页面、播放声音、执行静默任务等;坏处是如果App进程不在,消息大概率收不到,至少不能保证及时处理。
做技术选型时,一定要先确认:你要的是“让用户看到一条通知”,还是“让App后台更新数据”。很多需求表面上写着“推送消息到手机”,实际拆解后做的却是另一件事。比如App内小红点数量更新,完全可以在前台通过WebSocket更新,不需要走厂商推送通道;但用户关闭App后收到营销活动通知,那才需要系统级通道。
3. 技术选型:系统推送、厂商聚合与自建推送
3.1 主流推送方案对比
| 方案 | 维护方 | 核心优势 | 主要限制 | 适用场景 |
|---|---|---|---|---|
| APNs | 苹果 | iOS系统统一推送,进程被杀也能收到 | 只覆盖苹果设备,且需要配置证书和密钥 | iOS App |
| FCM | 安卓官方方案,功能完整 | 依赖Google Play服务,国内安卓设备接入范围有限 | 海外安卓应用 | |
| 厂商通道 | 小米、华为、OPPO、vivo等 | 系统级推送,各厂商后台保活能力强 | 需逐一接入各家SDK,各家能力不一致 | 国内安卓C端App |
| 聚合推送 | 极光、个推、友盟等 | 一套SDK封装多厂商通道,开发成本低 | 受第三方SDK稳定性影响,部分能力需付费 | 不想逐个适配厂商的团队 |
2759



被折叠的 条评论
为什么被折叠?



