Skip to content

推送网关技术全景

「消息从服务器到用户手机屏幕,中间经历了什么?」


一、技术背景:从"一问一答"到"主动推送"

1.1 HTTP协议的"先天性缺陷"

理解推送网关,要先理解它解决的根本问题:HTTP协议天生不支持服务端主动推送。

HTTP请求-响应模型:

客户端 → 发请求 → 服务器
客户端 ← 收响应 ← 服务器

每次通信必须由客户端主动发起
服务器无法主动"找"客户端

这意味着:如果服务器有新消息,客户端只能一遍遍去问——"有没有新消息?有吗?有吗?有吗?"

这就是"轮询"(Polling)。

1.2 轮询时代:短轮询与长轮询

方案原理问题
短轮询(Short Polling)客户端每隔1-3秒发一次请求问"有新消息吗"大量无效请求,浪费带宽和服务器资源
长轮询(Long Polling)服务器"挂起"请求,直到有新消息才返回服务器长时间占用连接资源;每次都要重新建立连接

轮询就像不断敲门问"到了吗?" ——可以工作,但效率极低,用户体验差。

1.3 WebSocket的诞生:全双工实时通信

2011年,HTML5标准将 WebSocket协议(RFC 6455)正式纳入规范,结束了这场"敲门问话"的历史:

HTTP时代:                          WebSocket时代:
客户端 → 请求 → 服务器              客户端 ↔ 服务器
客户端 ← 响应 ← 服务器              (双向实时通道,常开)
客户端 → 请求 → 服务器               一次握手,长期保持
客户端 ← 响应 ← 服务器              服务器随时主动推送

WebSocket的核心原理——协议升级握手:

第一步:客户端发送HTTP"升级请求":
GET /chat HTTP/1.1
Upgrade: websocket          ← 关键:我要升级协议
Sec-WebSocket-Key: dGhlIHNhbXBsZ...  ← 随机密钥

第二步:服务器验证并响应:
HTTP/1.1 101 Switching Protocols  ← 关键:协议切换成功
Upgrade: websocket
Sec-WebSocket-Accept: s3pPLMBiTxaQ...

第三步:连接升级完成,后续数据全走WebSocket帧

1.4 移动端推送的独特挑战

Web端有WebSocket就能解决大部分问题,但移动端(iOS/Android)面临的挑战远比Web端复杂得多

平台推送机制核心挑战
iOSAPNs(Apple Push Notification service)App在后台被系统挂起,无法主动通信;依赖系统统一唤醒
Android(海外)FCM(Firebase Cloud Messaging)依赖Google服务框架,国内无法使用
Android(国内)各厂商通道(华为、小米、OPPO、vivo)无Google服务,各厂商深度定制ROM,后台限制极其严格

二、核心技术问题

问题一:长连接保活——"连接还活着吗?"

问题描述:TCP连接看似建立了,但实际上中间经过的NAT设备、防火墙、运营商网关,可能会在无流量时主动断开空闲连接(通常15-30分钟)。

解决方案——心跳机制(Heartbeat)

NAT设备、防火墙、运营商网关都有可能主动断开空闲连接,把网络想象成一条有多道闸门的水管,这类需要长连接的服务,必须要有“心跳包”机制,定期发送一点点数据(就像隔一会儿放一滴水),就是为了告诉沿途所有的“管理员”:“这条水管还在用,请别关闸!”

心跳机制原理:
客户端每30秒发送一个心跳包(PING帧)
服务器收到后回复(PONG帧)
中间设备看到有流量 → 保持连接不被断开

智能心跳策略:
前台运行(App打开):心跳间隔 10-15秒
WiFi环境:心跳间隔 30-45秒
移动网络:心跳间隔 45-60秒(减少电量消耗)
检测到网络不稳定:缩短间隔,快速重连

问题二:进程保活——"App被系统杀了吗?"

问题描述:Android系统在内存紧张时,会优先杀掉后台应用。这意味着即便TCP连接建立着,如果App进程被杀了,连接就断了。

Android系统内存回收优先级:
后台进程(用户不可见)→ 随时可杀
空进程(无Activity/Service)→ 优先杀
系统进程 → 不杀

解决方案——多层次保活策略

层级策略说明
系统级厂商推送通道(华为、小米)系统级进程,系统不会主动杀
框架级前台服务(Foreground Service)显示持续通知条,保活优先级最高
调度级JobScheduler / WorkManager系统在合适时机批量执行任务
应用级自启动引导提示用户开启自启动权限

问题三:Android厂商通道碎片化

问题描述:这是国内Android推送最棘手的问题。没有Google Play Services,FCM完全不可用,国内各手机厂商各自维护自己的推送SDK,行为差异极大:

厂商SDK名称后台限制程度特殊要求
华为Push Kit严格需安装华为应用市场
小米Mi Push中等MIUI安全中心可设置白名单
OPPOOPPO Push严格需申请白名单
vivovivo Push严格系统级管控
荣耀Honor Push严格独立于华为系统

问题的核心:不同厂商的推送SDK行为不一致,有的设备能收到推送,有的收不到,排查成本极高。

解决方案 各家手机厂商维护着自己的系统级推送网关(如小米推送服务器、华为推送服务器),直接管理其品牌设备的系统长连接。这是真正的“推送网关”。 统一推播工作委员会推动标准统一;第三方推送服务商则建设了“路由网关”,负责将消息智能分发到正确的厂商网关。

问题四:WebSocket集群与会话共享

问题描述:WebSocket是有状态协议,一个客户端只与集群中某一个节点建立连接。当需要向某个用户推送消息时,消息必须路由到持有该用户连接的节点。

问题场景:
用户A → 连接节点1
用户A → 连接节点3
服务端发消息给用户A → 消息发到节点2
节点2:用户A不在这台 → 消息丢失!

解决方案——广播方案对比

方案原理优点缺点
注册中心方案所有节点上报连接状态到中心存储精确路由实现复杂,依赖中心存储
消息队列广播方案发一条消息,所有节点都收到,各自判断实现简单,轻量节点多时广播量大
Redis Pub/Sub订阅同一频道,节点收到广播实时性高不保证可靠送达

问题五:消息去重与跨端同步

问题描述:当用户同时在手机、平板、网页上登录同一账号,同一条消息通过长连接推送多次,会导致用户看到重复通知。

场景:
用户用手机和电脑同时打开App
收到消息 → 手机收到1次 → 电脑收到1次
用户看手机通知 → 电脑又弹一次通知 → 体验糟糕

解决方案——BroadcastChannel + 消息ID去重

javascript
// 每条消息携带唯一ID
const seenIds = new Set();

function handleNotification(notification) {
  if (seenIds.has(notification.id)) return; // 已有,跳过
  seenIds.add(notification.id);
  showToast(notification);
}

// 跨标签页协调:同一个标签页收到,其他标签页跳过
const channel = new BroadcastChannel("notifications");

function handleNotification(notification) {
  if (seenIds.has(notification.id)) return;
  channel.postMessage({ type: "claimed", id: notification.id }); // 广播"我处理了"
  showToast(notification);
}

channel.onmessage = (event) => {
  if (event.data.type === "claimed") {
    seenIds.add(event.data.id); // 其他标签页不再重复显示
  }
};

问题六:消息优先级与送达率保障

问题描述:并非所有消息都同样重要。支付失败、验证码这类"关键消息"必须送达,但社交动态、活动推送送达延迟一点也没关系。

解决方案——消息分级策略

优先级消息类型推送策略
Critical(关键)支付失败、安全警报WebSocket+推送双通道,立即送达,勿扰模式也要响
Important(重要)新消息、@提及WebSocket优先,断连后5-10秒走推送通道
Informational(普通)社交动态、活动通知仅WebSocket,离线不补发
Background(后台)配置更新、数据同步静默推送,App后台处理,不打扰用户

三、技术架构详解

3.1 整体架构图

┌─────────────────────────┐
│      业务服务器          │
│ (订单/聊天/支付/客服...) │
└───────────┬─────────────┘
            │ 写消息
┌───────────▼─────────────┐
│       消息队列           │
│   (Kafka / RocketMQ)    │
└───────────┬─────────────┘
            │ 消费
┌───────────▼─────────────┐
│推送网关(各 APP 服务端自建)│
│  ┌─────────┬─────────┐  │
│  │WebSocket│ HTTP    │  │
│  │  网关   │ 推送接口 │  │
│  └────┬────┴────┬────┘  │
└───────┼─────────┼───────┘
        │         │
   ┌────┼─────────┼────────────┐
   │    │         │            │
   ▼    ▼         ▼            ▼
┌──────┐┌──────┐┌──────┐┌──────────┐
│iOS   ││华为   ││小米  ││ Web端    │
│APNs  ││Push  ││Mi    ││WebSocket │
│      ││Kit   ││Push  ││          │
└──────┘└──────┘└──────┘└──────────┘
(各手机厂家自己的推送网关)

3.2 推送网关核心模块

模块职责技术选型
长连接管理维护与客户端的TCP连接,处理心跳、断线重连Netty(Java)/ Go / Node.js
会话管理记录每个用户/设备与哪个节点建立了连接Redis(分布式会话)
消息路由根据设备类型选择最优推送通道本地路由表 + 配置中心
消息队列解耦业务与推送,实现削峰填谷Kafka / RocketMQ
厂商通道对接对接APNs/FCM/华为/小米等厂商接口各大厂商SDK
监控报警监控送达率、延迟、错误率Prometheus + Grafana
消息存储离线消息持久化,连接恢复后补发MySQL + Redis

3.3 推送完整流程

步骤1:消息触发
  用户A在App发消息 → 业务服务器写消息到数据库
  业务服务器 → 写入消息队列(带目标用户ID)

步骤2:网关路由
  推送网关消费消息队列 → 查询目标用户的设备列表
  查询该用户有哪些设备在线(手机/平板/Web)

步骤3:多通道分发
  在线设备 → 通过WebSocket长连接推送(毫秒级)
  离线设备 → 根据设备类型路由到对应厂商通道
    iOS设备 → APNs
    华为设备 → 华为Push Kit
    小米设备 → Mi Push
    其他Android设备 → FCM

步骤4:送达确认
  设备收到消息 → 发ACK确认
  超时未ACK → 进入重试队列 → 指数退避重试
  多次重试失败 → 记录失败原因,触发报警

3.4 APN和 FCM 的大白话解释

APNs和FCM本质上就是“推送网关”,都是让一个系统级的、常驻的“信使”来统一接收和分发消息,从而绕开单个App后台被限制的问题。

  • Apple APNs 统一连接:你的iPhone开机联网后,系统会与苹果的APNs服务器建立一个唯一、持久、加密的长连接。这个连接由iOS系统本身维护,不受单个App是否在后台运行的影响。 设备令牌:当你的App第一次请求推送权限时,系统会向APNs服务器为这台设备注册,并获取一个唯一的设备令牌。App将这个令牌发送给自己的服务端保存。 推送流程: 你的服务端想发推送时,将消息内容和设备令牌一起发送给APNs服务器。 APNs服务器通过系统维护的那个长连接,精准找到对应的设备。 iOS系统收到消息后,先验证其合法性,然后根据令牌中的App标识,将消息唤醒或直接显示给对应的App。

  • Google FCM 统一连接:Android设备上的Google Play服务会与FCM服务器建立一个持久的长连接。这个连接也是系统级维护的。 注册令牌:与APNs类似,App会从FCM获取一个注册令牌,并传给自己的服务端。 推送流程: 你的服务端将消息和注册令牌发给FCM服务器。 FCM服务器通过Google Play服务建立的长连接,将消息推送到设备。 Google Play服务将消息分发给目标App。对于高优先级的消息,它甚至会唤醒App的后台进程来处理。

  • 两者的关键共同点: “信使”常驻:iOS系统或Google Play服务作为永远在线的“信使”。 App无需自建长连接:每个App不需要自己在后台保活一个耗电的网络连接,由系统统一高效管理。 令牌寻址:通过令牌机制,实现从你的服务端到用户设备的精准寻址。

  • 大白话总结 你可以把它想象成小区快递柜: 以前(没有APNs/FCM):每个快递员(每个App的服务端)都必须打电话叫你自己下楼取件(App自己保持后台连接)。你不可能同时接所有电话,很多快递就收不到了。 现在(有了APNs/FCM):小区建了一个统一的快递柜(苹果/谷歌的推送服务器),并配了一个24小时在线的管理员(iOS系统/Google Play服务)。所有快递都先送到这个快递柜。管理员收到快递后,会根据房号(设备令牌)把包裹放到对应的柜格里,然后自动发短信通知你(系统通知)。你只需要在有空时去柜子取一次就行。 这样,快递员(App服务端)省事了,你(手机用户)也不用被频繁打扰,而且所有快递都能收到,还特别省电。


四、关键技术选型对比

4.1 长连接协议对比

协议适用场景优点缺点
WebSocketWeb端实时通信全双工、轻量、广泛支持无持久化,断连需重建
MQTTIoT设备、移动端极轻量(2字节头部)、支持QoS、功耗低协议较复杂,需要Broker
Socket.IO快速原型、跨平台自动降级、兼容性好体积较大,有自定义封装
gRPC Streaming微服务间通信高性能、跨语言、标准化复杂度高,不适合公网推送
SSE(Server-Sent Events)单向推送(服务端→客户端)轻量、基于HTTP、自动重连仅单向,IE不支持

4.2 开源推送框架对比

框架开发语言特点适合场景
NettyJava高性能事件驱动,最成熟大规模生产环境
GoimGo轻量、高并发,字节跳动开源日活千万级App
MobileIMSDKJava/iOS/Android/小程序跨平台完整方案快速集成
OnePushAndroid多厂商通道统一SDK国内Android推送聚合

五、最新进展(2025—2026年)

5.1 多通道融合成为行业标准

2025年后,单一推送通道已无法满足业务需求,多通道融合成为行业共识:

传统方案:
  国内Android → 只接厂商通道 → FCM不可用时完全失效

融合方案(2026年主流):
┌─────────────────────────────────────────────┐
│          智能通道选择引擎                     │
│                                              │
│  设备上线 → 健康度检测 → 最优通道自动选择     │
│                                              │
│  检测华为可用 → Push Kit (主通道)             │
│  检测FCM可用 → FCM (备用通道)                │
│  检测自研通道 → 自建TCP长连接 (实时通道)      │
│                                              │
│  主通道失败 → 自动切换备用通道 → 无感知       │
└─────────────────────────────────────────────┘

5.2 AI驱动的推送优化

2025年起,头部厂商开始将AI能力引入推送系统:

方向具体应用效果
用户行为预测AI分析用户活跃时段,动态调整推送时间打开率提升30-50%
消息优先级智能判断NLP分析消息内容,自动分级减少无效推送,降低卸载率
送达率预测预测消息是否能成功送达,提前预警运维响应提前30分钟
异常检测AI识别异常送达率模式,自动诊断MTTR(故障恢复时间)缩短60%

5.3 鸿蒙生态推送:华为Push Kit新能力

2026年,华为Push Kit持续演进,新增三类高价值场景:

新能力场景突破点
实况窗推送打车/外卖/快递实时状态可折叠设备实时刷新,无需打开App
应用内通话推送视频通话、语音通话系统级通话体验,接通率大幅提升
卡片刷新消息打车状态、订单进度服务直达,减少跳转层级

5.4 统一推送协议探索:MQTT 5.0

2025年后,MQTT 5.0在移动推送领域的应用逐渐增多,相比MQTT 3.x有显著改进:

改进点说明对推送的价值
用户属性(User Properties)消息可携带自定义键值对推送元数据直接内嵌,无需解析消息体
主题别名(Topic Alias)减少主题名字节开销在高频推送场景节省30%带宽
原因码(Reason Code)明确的错误原因故障诊断效率大幅提升
消息过期(Message Expiry)消息可设置过期时间离线消息自动过期,无需手动清理

5.5 推送安全与合规

议题2026年新要求影响
隐私合规GDPR/CCPA/国内数据安全法趋严推送内容不得包含敏感个人信息
端到端加密高安全场景要求推送内容仅客户端可解密推送仅携带消息ID,正文由App拉取后解密
DDoS防护恶意刷Token攻击增加网关需实现Token请求限流(1000次/分钟/设备)

六、精华总结

推送网关的核心矛盾

用户期望:
  → 消息秒到、省电、不打扰

系统现实:
  → 网络不稳定、厂商限制、电量管控

推送网关的使命:
  → 在"用户期望"和"系统现实"之间,
     找到那个恰到好处的平衡点。

架构选型决策树

消息实时性要求?

├── 毫秒级(聊天、直播)→ WebSocket长连接 + 自研推送
│   → 多节点集群 + Redis会话共享

├── 秒级(订单通知)  → 厂商通道 + 长连接备用
│   → 华为/小米/APNs多通道聚合

└── 分钟级(日报推送)→ 厂商通道即可
    → 无需长连接,节省资源

参考资料

Move fast and break things