1. 项目背景与需求痛点
消息推送系统是现代应用开发中不可或缺的基础设施。但在实际开发中,我们经常会遇到几个典型问题:
- 消息延迟严重:传统轮询方式造成资源浪费,长连接又难以维护
- 离线消息丢失:用户断网期间的消息无法有效送达
- 协议碎片化:不同平台(iOS/Android/Web)需要对接不同推送服务
- 开发成本高:需要为每个平台单独实现推送逻辑
我团队在开发跨平台应用时就深陷这些泥潭。某次线上事故直接促使我们决定自研解决方案——当时关键告警消息延迟达37分钟,直接导致五位数的经济损失。经过三个月的迭代,我们开发出了这套名为Bark的全能消息推送系统。
2. 架构设计与技术选型
2.1 整体架构
Bark采用分层设计(图示见下文),核心包含:
[客户端SDK] ←WebSocket→ [网关层] ←gRPC→ [消息处理集群] → [持久化存储] ↑ [管理控制台]2.2 关键技术选型
连接层:
- 选用WebSocket+MQTT协议组合(对比测试见下表)
- 自研连接保持算法:心跳间隔动态调整(2-60秒)
| 协议类型 | 平均延迟 | 断线重连成功率 | 移动端耗电 |
|---|---|---|---|
| 纯WebSocket | 218ms | 92% | 中 |
| MQTT | 156ms | 98% | 低 |
| 混合模式 | 172ms | 96% | 中低 |
消息处理:
- 消息队列:Kafka(分区策略优化后吞吐提升40%)
- 流处理:Flink实现实时去重和优先级排序
离线存储:
- 采用分级存储策略:
- 热数据:Redis(最近24小时)
- 温数据:MongoDB(7天内)
- 冷数据:对象存储(S3协议兼容)
3. 核心功能实现细节
3.1 多协议接入网关
网关层需要处理不同协议的转换:
// 协议转换核心逻辑示例 func convertProtocol(msg Message, target Protocol) ([]byte, error) { switch target { case APNS: return buildAPNSPayload(msg) case FCM: return buildFCMPayload(msg) case WebPush: return buildWebPushPayload(msg) default: return nil, ErrUnsupportedProtocol } }关键优化点:
- 协议缓存池减少60%内存分配
- 异步IO提升3倍吞吐量
3.2 离线消息处理流程
- 检测设备状态(最后一次活跃时间)
- 消息进入待发送队列
- 触发重试机制(指数退避算法)
- 持久化到冷存储(超过TTL)
重要提示:离线消息默认保存7天,重要业务建议设置自定义TTL
3.3 消息优先级系统
我们定义了5个优先级等级:
- 紧急(立即发送,重试间隔30秒)
- 高(5分钟内送达)
- 中(1小时内送达)
- 低(24小时内送达)
- 延迟(指定时间发送)
优先级会影响:
- 队列处理顺序
- 重试策略
- 存储位置选择
4. 性能优化实战
4.1 连接保持优化
移动端网络环境复杂,我们实现了:
- 智能心跳检测(根据网络质量动态调整)
- 多路复用(单个TCP连接承载多个逻辑通道)
- 快速重连(会话状态缓存)
优化前后对比:
[优化前] 平均断线率:8.7% | 重连耗时:2.3s [优化后] 平均断线率:1.2% | 重连耗时:0.4s4.2 消息压缩传输
针对大消息(如图片通知):
- 采用Zstandard压缩算法
- 客户端SDK内置解压能力
- 压缩阈值动态调整(默认>1KB)
测试数据:
原始大小:2.8MB → 压缩后:463KB(压缩率83%) 传输时间从1.4s降至0.3s5. 部署与运维方案
5.1 集群部署建议
生产环境推荐配置:
- 网关节点:至少3实例(跨可用区)
- 消息处理:按每万TPS 2个Worker配置
- Redis集群:主从+哨兵模式
我们使用的K8s部署模板:
apiVersion: apps/v1 kind: Deployment metadata: name: bark-gateway spec: replicas: 3 strategy: rollingUpdate: maxSurge: 1 maxUnavailable: 0 template: spec: containers: - name: gateway resources: limits: cpu: "2" memory: 2Gi5.2 监控指标
必须监控的核心指标:
- 端到端延迟(P99 < 500ms)
- 消息积压量(预警阈值:10,000)
- 在线设备数(容量规划依据)
- 错误率(按类型分类统计)
我们使用Prometheus+Granfana的监控看板包含:
- 实时消息吞吐量
- 各协议成功率
- 资源使用率热力图
6. 客户端集成指南
6.1 Android端配置
- 添加依赖:
implementation 'com.bark.push:sdk:1.3.0'- 初始化代码:
Bark.init(context, config = Config( appKey = "YOUR_KEY", heartbeatInterval = 30, // 秒 logLevel = if (BuildConfig.DEBUG) DEBUG else ERROR ) )6.2 iOS注意事项
特殊处理点:
- 后台模式配置:
<key>UIBackgroundModes</key> <array> <string>remote-notification</string> <string>voip</string> </array>- 确保开启Push Notification capability
- 建议使用Notification Service Extension处理富媒体
7. 踩坑实录与解决方案
7.1 消息重复问题
现象:某些设备收到重复推送(2-3次)根因:MQTT QoS1模式下服务端未及时收到PUBACK解决方案:
- 实现消息去重表(基于msgID+deviceID)
- 客户端增加本地去重缓存
- 调整QoS级别策略
7.2 安卓保活难题
挑战:国产ROM后台限制严格应对策略:
- 使用Foreground Service(需常驻通知栏)
- 接入各厂商推送通道(小米、华为等)
- 引导用户关闭电池优化(需特殊权限)
7.3 协议兼容性陷阱
案例:某厂商设备WebSocket实现不符合RFC标准应对方案:
- 增加协议兼容模式开关
- 自动降级机制(WebSocket → HTTP长轮询)
- 设备特征指纹识别系统
8. 扩展能力设计
8.1 消息回执系统
实现闭环确认机制:
- 客户端收到消息后发送ACK
- 服务端标记消息状态
- 控制台展示送达率统计
核心数据结构:
{ "msg_id": "uuidv4", "status": "delivered|read|failed", "timestamp": 1620000000, "device_info": {...} }8.2 智能路由策略
基于设备特征的动态路由:
- 网络类型(WiFi/蜂窝)
- 电量水平(>30%才发送大文件)
- 本地时间(勿扰模式时段静默)
路由规则DSL示例:
{ "rule": "battery < 15 AND !charging", "action": "delay", "params": {"until": "next_charge"} }这套系统上线后,我们的关键业务消息延迟从平均12秒降至800毫秒以内,离线消息送达率达到99.3%。最让我自豪的是,有次服务器机房断电4小时,恢复后所有离线消息在90秒内全部补发成功,零丢失。现在这套系统已经稳定运行19个月,日均处理消息23亿条。