写点什么

我们是怎么做到每小时推送百万级通知的

  • 2019-12-03
  • 本文字数:2085 字

    阅读完需:约 7 分钟

我们是怎么做到每小时推送百万级通知的

推送通知是让用户立即接收到事件的一个非常有效的工具。在 Gojek,我们每天需要处理 300 多万个订单,跨 20 多款产品。


可以想象的是,我们每天推送的通知数量有多大——大概每小时 1 百万个。这篇文章将介绍我们在处理如此体量的推送通知时所面临的挑战,以及我们的解决方案。


体量大还只是其中的一个方面,在 Gojek,我们还需要面对一些独有的问题。

多个应用程序

Gojek 不只有一个 App,除了用户 App,我们还有 GoLife、司机 App、商家 App,还有服务商 App。



当我们的系统要推送通知,要么是推给某个用户的 App(例如,推给 GoLife 的通知不会被推到 Gojek 上),要么是推给所有的 App(例如促销通知)。


我们的系统需要足够灵活,能够在广播和单独推送之间自由地选择。

多个通知服务提供商

因为我们的用户 App 需要支持 iOS 和 Android 两个平台,所以也需要支持多个通知系统。


Android 平台我们使用了 FCM(Firebase Cloud Messaging)和 GCM(Google Cloud Messaging),iOS 平台我们使用了 APNS(Apple Push Notification Service)。



每一个通知服务提供商都为不同的 App 提供了不同的 API 秘钥和令牌。例如,GoLife 和 Gojek 使用的 FCM API 秘钥就不一样。

一个用户多个设备

我们允许一个用户同时登录多个设备,所以通知需要被推送给用户已登录的所有设备上,这就存在之前的两个问题:


  1. 用户可以在单个设备上登录多个 App(Gojek 和 GoLife);

  2. 用户可能会登录多个设备,每个设备需要使用不同的通知服务提供商。例如,用户可以在 Android 设备和 iOS 设备上登录 Gojek。

多个需要推送通知的服务

Gojek 采用了微服务架构,我们想要让每个服务都能推送通知,不需要操心多设备和多服务提供商问题。



为了解决上述问题,并尽可能保持 API 简单,我们的通知系统被分为三个组件:


  1. 通知服务器——提供通知推送 API,并将通知推送到作业队列中;

  2. 令牌存储——保存已登录用户的设备和设备令牌数据;

  3. 通知处理器——处理作业队列中的消息,并将消息发送给通知服务提供商。



每个组件都解决了上述的一部分问题,接下来,我们来深入介绍这些组件。

令牌存储

用户在登录 App 后,App 会使用设备令牌和 App ID 调用令牌存储 API。



在用户退出时,这些记录会被删除。


令牌存储用于决定向用户的哪些设备推送通知。

通知服务器

这是 HTTP 服务器,提供用于推送通知的 API。


为了简单起见,API 要求把用户 ID 和 App ID 放在 HTTP 头部,把通知信息放在请求体里:


POST http://<base_url>/notificationuser_id: <user_id>application_id: <application_id>{  "payload": {},  "title": "You driver is here",  "message": "Please meet your driver at the pickup point"}
复制代码


服务器从令牌存储获取所有的用户设备信息,然后为每个用户设备安排一个调度作业。


通知服务器为系统提供了外部接口,需要推送通知的服务只要通过用户 ID 来调用它的 API,剩下的事情由通知服务负责处理。

作业队列

我们使用 RabbitMQ 作为作业队列,并为每一种 App ID 和通知类型创建了单独的队列。



分配单独的队列是很重要的,因为我们要为每一种 App 和通知类型做好故障隔离。例如,如果 com.gojek.app 的 FCM 令牌过期,就不会影响到 com.gojek.life 或者 com.gojek.driver.bike 的作业。

通知处理器

处理器进程从作业队列里拉取消息,把它们发送给对应的通知服务提供商。


为了保持代码简单,并能够支持不同的服务提供商,我们定义了统一接口:


type PushService interface {  Push(ctx context.Context, m PushRequest) (PushResponse, error)}
复制代码


Push 方法接收一个请求对象,并返回一个响应对象。


请求对象包含了与接收方和通知(比如过期时间、标题和文本)有关的信息。


type PushRequest struct {  DeviceID   string   Title      string  Message    string  Payload    map[string]interface{}  //其他参数}
复制代码


响应消息里包含了通知是否发送成功的信息:


type PushResponse struct {  Success         bool  ErrorMsg        string}
复制代码


然后为不同的服务提供商实现接口。例如,FCM 和 APNS 对应的实现看起来像下面这样:


type FCMProvider struct {  // 配置信息,比如API令牌和URL端点}func (p *FCMProvider) Push(ctx context.Context, m queue.Message) (notification.PushResponse, error) {  // 发送通知给FCM服务器}type APNSProvider struct {  // 配置信息,比如API令牌和URL端点}func (p *APNSProvider) Push(ctx context.Context, m queue.Message) (notification.PushResponse, error) {  // 发送通知给APNS服务器}
复制代码


通知处理器负责选择对应的通知服务提供商,并将消息发送给它们。

结论

在面对这些挑战时,我们找出其中的一些常用模式,把它们抽离成不同的服务,将一个相对复杂的问题变成了一系列简单且易于管理的服务。


每当一个核心逻辑需要不同的实现时,我们就把它抽离成单独的服务:


  • 多个设备问题通过令牌服务来解决;

  • 多个 App 问题通过统一的通知服务器接口来解决;

  • 多个通知服务提供商通过单独的作业队列和通知处理器来解决。


最终,我们构建了一个每小时能够处理 1 百多万个推送通知的系统。

原文链接

How We Manage a Million Push Notifications an Hour


2019-12-03 11:147609
用户头像
小智 让所有人认同的文字称不上表达

发布了 408 篇内容, 共 399.5 次阅读, 收获喜欢 1985 次。

关注

评论

发布
暂无评论
发现更多内容

HarmonyOS NEXT 中级开发笔记:电器管家应用的数据库设计与操作实践

bianchengyishu

HarmonyOS NEXT

C 语言宏定义原来可以玩出这些花样?高手必看!

伤感汤姆布利柏

数据分析与AI丨基于AI的电子元件焊接质量优化

Altair RapidMiner

机器学习 AI 数据分析 制造业 RapidMiner

通义灵码 Rules 来了:个性化代码生成,对抗模型幻觉

阿里云云效

阿里云 云原生

为什么 Apache Doris 是比 Elasticsearch 更好的实时分析替代方案?

SelectDB

搜索引擎 全文检索 elasticsearch 日志分析 Doris

开发提测流程优化思考:如何在质量控制与效率间寻找平衡?

TechLead Studio

EviMed:左手综述内容,右手参考文献!三步产出可溯源的万字医学综述!

科技汇

SvelteKit 最新中文文档教程(16)—— Service workers

冴羽

Vue 前端 React Svelte SvelteKit

Databend 产品月报(2025年3月)

Databend

突破公网瓶颈的专线选择:IPLC专线

Ogcloud

企业组网 IPLC 国际专线 国际网络专线 国际IPLC专线

HarmonyOS NEXT 中级开发笔记:电影票务应用的数据库设计与实践

bianchengyishu

HarmonyOS NEXT

HarmonyOS NEXT 中级开发笔记:智力象棋应用的数据库设计与实践

bianchengyishu

HarmonyOS NEXT

《Operating System Concepts》阅读笔记:p512-p527

codists

操作系统

天下拍-艺术品拍卖经典案例分享

至存网络

埋点 拍卖 用户画像 艺术品拍卖 资产拍卖

「高盛」最新人形机器人研报:主流人形机器人公司梳理和商业化瓶颈(附报告)

机器人头条

机器人 科技 大模型 人形机器人 具身智能

HarmonyOS NEXT 中级开发笔记:电竞直播应用的数据库设计与实践

bianchengyishu

HarmonyOS NEXT

反向海淘代购中二次付款难题的深度解析与解决之道

代码忍者

HarmonyOS NEXT 中级开发笔记:家庭菜谱应用的数据库设计与实践

bianchengyishu

HarmonyOS NEXT

C 语言内存布局深度剖析:从栈到堆,你真的了解吗?

不在线第一只蜗牛

C#

时序数据库 IoTDB 荣获第八届中关村国际前沿科技大赛工业互联网领域赛 Top3

Apache IoTDB

通义灵码 Rules 来了:个性化代码生成,对抗模型幻觉

阿里巴巴云原生

阿里云 云原生

第九届华为ICT大赛中国总决赛收官 84支队伍晋级全球总决赛

极客天地

电竞革命!电选对平台狂省90%硬件费:ToDesk云电脑、网易云游戏等终极评测

小喵子

云电脑 ToDesk云电脑 云电竞

卧槽!C 语言宏定义原来可以玩出这些花样?高手必看!

不在线第一只蜗牛

C#

HarmonyOS NEXT 中级开发笔记:修车助手的数据库设计与实践

bianchengyishu

HarmonyOS NEXT

Flink批处理自适应执行计划优化

Apache Flink

大数据 flink 实时计算 批处理

C 语言内存布局深度剖析:从栈到堆,你真的了解吗?

伤感汤姆布利柏

看过智谱现场演示,我觉得AI要开始卷“动手能力”了

Alter

AI

HarmonyOS NEXT 中级开发笔记:出行地图应用中的数据库设计与实践

bianchengyishu

HarmonyOS NEXT

HarmonyOS NEXT 中级开发笔记:艺术展览应用的数据库设计与实践

bianchengyishu

HarmonyOS NEXT

昇腾+DeepSeeK | 博云联合昇腾打造满血版一体机

BoCloud博云

博云 DeepSeek AI一体机

我们是怎么做到每小时推送百万级通知的_架构_Soham Kamani_InfoQ精选文章