找回密码
 立即注册
首页 业界区 安全 腾讯新闻APP的消息推送Push架构技术重构实践 ...

腾讯新闻APP的消息推送Push架构技术重构实践

杓疠? 昨天 20:05
本文由腾讯技术团队颜勇分享,原题“腾讯新闻PUSH架构升级之路”,有修订和重新排版。
1、引言

68 万行代码精简到8.6 万;Golang 重写大部分 C++模块;解决过度微服务化问题…… 这是新闻 PUSH 架构团队取得的技术收益。
PUSH 是腾讯新闻精品资讯的重要分发途径,也是新闻 App 重要的促活手段。作为 PUSH 架构团队,我们一方面在积极支持好新闻护盘,同时也在对 PUSH 架构进行不断的升级与进化,以持续提升 PUSH 系统的稳定性与质量、研发效率,同时持续减少运营成本。
本文主要分享的是腾讯技术团队近年来对腾讯新闻消息推送PUSH系统做的架构优化和技术实践。
1.png
2、Push平台介绍

2.1 概述

PUSH 是腾讯新闻内容重要的分发渠道,新闻 PUSH 平台承担着将新闻资讯触达到新闻用户、满足用户及时获取精品资讯的需求。
2.jpeg
总体上,新闻 PUSH 链路分为下面两部分。
2.2 PUSH触发

按触发方式的不同,新闻 PUSH 分为三类:

  • 1)人工 PUSH:运营在 push cms 系统指定要发送的文章、要触达的人群包,人工触发push发送;这类 PUSH 目前主要用于推送热点事件/热点资讯等;
  • 2)自动化 PUSH:周期性地给用户计算他可能感兴趣的内容,这类推送由后台自动触发;
  • 3)功能性 PUSH:由业务系统触发,主要是为了实现一些业务功能通知,比如评论通知、关注通知等。
2.3 PUSH下发

对于所有 PUSH 触发 的PUSH 进行调度(包括避让、打散和频控等)和触达(通过自有通道或厂商通道推送给用户)。
新闻业务对新闻 PUSH 平台最重要的要求是:
1)要保证精品咨讯触达的及时性:
新闻 PUSH 最重要的是要体现“新”,因为腾讯新闻用户有及时获取热点/突发资讯的诉求,用户经常有这样的体感,有热点突发事件时,所有 App 都会尝试第一时间向用户发起推送,用户大概率会点击收到的第一个推送。在了解了相关热点事件后,对于后续其它 App 的推送,对用户而言就没信息量了,大概率会被忽略,甚至可能会被用户视为一种打扰,影响用户体验。从我们实验数据来看,当P USH 下发延迟降低 50%,PUSH 点击量会提升 10%。
所以新闻 PUSH 一直以来的目标是:热点资讯需要第一时间触达给用户,要做到“全网首推”。
2)要保证推送的用户体验和较好的拉起效率:
PUSH 是新闻重要的促活手段,需要有较好的促活效率,这要求保证用户较好的推送体验,因为用户如果感觉推送体验不好,用脚投票,把 App 的 PUSH 系统开关给关了,这对 PUSH 而言就基本上就永远丧失了给这个用户推送的机会了。这就要求要尽量保证在合适的时间点给推送用户感兴趣的内容,推送要有合理的频次,相邻 PUSH 之间要有合理的时间间隔,推送内容要做合适的打散。
其实这两个要求其实在一定层面上是有冲突的:

  • a.如果要保证推送的及时性,就要求尽量减少计算,拿到消息消息后无脑推到消息通道,这个肯定最快;
  • b.如果要保证良好的推送用户体验,就需要做很多的判断、考量和计算,这些考虑越多就需要做更多的计算和 io 操作,会影响推送的及时性;最近几年,业务成本的考虑也是 PUSH 关注的重点,需要削减使用的机器和资源,就要求用更少的机器如何发得更快更好。
总结而言,之前新闻 PUSH 业务的突出问题主要有两个方面,请继续往下阅读。
3、Push平台问题1:推送速度慢

我们团队从 2022 年年中开始接手新闻 PUSH 平台。交接工作刚启动,就遇到了一次 S 级热点事件——一个国际级突发新闻。那天晚上,全网用户都在密切关注它的最新进展。
这个事件有两个特点:热度极高、且并非完全突发——早在一个月前就已经有明确预告,因此运营部门提前布置了应急预案。我们刚接手系统时,对整个下发链路还不够熟悉,只能凭直觉扩容机器,希望能抗住峰值。结果现实很快给了我们一记当头棒喝。
当晚,很多内部同事都装着多个新闻 App,一眼能看到谁家的推送更快。那晚我们的延迟问题非常明显,甚至有用户在热点过去一个多小时后才收到通知。事后有专门的评测团队做了分析,指出“PUSH 下发耗时过长,高活用户 P90 均值达 20 分钟”,报告还发到了高层群里——对我们来说,那无疑是一次刻骨铭心的教训。
4、Push平台问题2:开发效率和问题排查效率低

之前 PUSH 链路特别长,新闻 PUSH 内部有 30+ 个模块,同时还依赖其它两个跨业务团队。经常一个需求开发要改多个模块,要团队几个人一起开发,约定交互协议,开发后再联调测试,在多个模块起联合实验;然后还得给中台提需求,然后匹配中台的排期后,才能完成需求上线;这一系列操作就拉长了 push 需求的leadtime。
线上有 case 时,问题排查也需要串联多个模块,关联多个模块数据,甚至需要跨部门拉上其它这边来一起来排查,排查效率非常低。push case 非常多,比如用户为什么收到了/没收到某条 push 之类的典型 case,之前需要关联链路20来个模块的日志,还要联合中台一起排查,每次 case 排查时间都在天级;之前在case 排查上,每天都耗费我们大量的人力。
既要持续提升 PUSH 触达的及时性、又要持续提升推送的用户体验和拉活效率,还要持续降低运营成本,客观而言,在技术上是一个较大的挑战。本文主要详述,我们如何通过技术架构升级来支撑这个既要&又要&还要的目标。
5、老Push架构的问题梳理

5.1 模块链路过长,内耗过多

一条快速PUSH,从推送内容过审后,到最终发出去,最长要经过18个模块,另外还需要经过中台多个模块。一条待推送的数据最多要经历 17 次内部 rpc 转发,多个模块之间腾挪流转,各种网络 rpc,各种内耗,肯定发得慢。
一个最典型的例子:原架构有个模块叫scheduler,它主要负责决定一个push该不该发,直观上感觉它里面应该囊括了各种过滤策略,但是原架构做成了多个微服务。scheduler 模块里本身有一些过滤逻辑,另外有一个叫做 filter 的模块,专门负责品牌、开关等硬规则过滤;另外有一个叫做 policy 的模块,专门负责配额等软规则过滤;所有过滤规则都通过后,进入一个叫做 channer 模块,就决定下这次推送走哪个通道;然后又走到一个叫 worker 的模块里,而它只做对接下游中台的协议适配。
总体上看,原链路就是过度微服务化了:

  • 1)模块多会导致数据流转的低效,模块间网络 rpc 会浪费处理耗时;
  • 2)其次会影响迭代效率,模块数不是越多越好,因为经常一个需求需要改多个模块,做多次上线;
  • 3)同时模块过多也对联调&测试效率,影响线上 case 排查效率。
这就违反了“模块内高内聚,模块间低耦合”的架构设计原则,进而会影响业务迭代效率。
5.2 依赖服务有瓶颈

上文提到的 S 级热点事件时,我们将下发服务机器扩了一倍,但是下发速度并没有提升,说明瓶颈不在下发服务本身下,而是在依赖服务上;通过链路debug,我们定位到了链路瓶颈:号码包拉取。
在发送人工 push,运营会指定受众人群包(几百万到几亿不等),这时候需要分页拉取该号码包数据进行处理。
之前老架构使用了底层平台的人群包服务,新闻所有 push 人群包都上传到了该人群包服务,当发送指定人群包,需要请求平台侧接口分页拉取人群包数据,当时因为平台侧人群包功能实现比较复杂,能支持一些比较高级的能力,因此这个分页接口耗时比较长。但其实我们只用到了最简单的数据分页的功能,完全可以采用更简单的实现方案,以减少接口耗时。
5.3 链路稳定性不好

5.3.1)容错能力差:
之前链路基本无容错能力,发生了过一次因上游未按约定协议跟我们请求交互,导致我们服务挂了半天,是一次典型的 P0 级事故。
5.3.2)缺少节点自动故障转移:
scheduler 负责 push 调度,原架构为了提升处理效率,scheduler 里做了本地缓存;为了避免缓存失效,起了一个服务 dispatch 消费触发侧生产的待推送的消息,然后按照用户设备号一致性哈希来 sharding,通过 rpc 请求对应的 scheduler,scheduler接受到请求后,塞入到它本地的内存队列里,如果队列满了就直接丢弃。
它原来存在有这些问题:dispatch无脑往下游转发,sharding规则非常僵硬,一个用户的push一定要打到某个节点,未做故障转移;当某节点异常满载时,dispatch还是会往这个节点打,导致丢消息或者是 push发送得慢。而且当节点满载时,有限的cpu还需要耗费在rpc解包、无法插入内存队列而丢弃之类的无用消耗上。
5.4 链路处理无优先级区分

运营人工发的 PUSH 和自动化 PUSH 都使用同一个下发链路,热点突发事件资讯多由运营人工发送,而自动化 PUSH 多发一些用户可能感兴趣的内容,其实它对于推送速度并没那么敏感;当有人工推送的热点突发内容时,自动化 PUSH 会和它一起争抢有限的链路资源。
另外,在链路总吞吐量一定的情况下,其实处理顺序可以调整,让链路资源有限保证人工推送的热点突发内容的发送;
5.5 技术栈不统一

之前 push 下发链路有 C++/Go 两种技术栈,技术栈不统一不利于代码复用,影响需求迭代效率。push下发链路本质上是一个高 io 型的流程,其实可以完全可以统一到 Golang 技术栈。
5.6 链路测试效率低

push 链路业务逻辑比较多,在日常密集业务需求迭代中,新功能我们可以在线上通过构造对应的功能 case 来进行冒烟测试,但是比较难评估是否影响了线上已有的业务逻辑。
之前缺乏有效的回归测试手段,由于担心影响线上业务指标,为了验证是否影响线上已有业务逻辑,我们大的修改都会开比较长的小流量实验验证,比如我们在做调度架构升级时,开了一个近两个月的小流量实验,测试效率比较低也会导致需求迭代效率比较低。
6、新Push架构优化1:消息通道自建

之前新闻 PUSH 依赖于平台侧的消息通道,业务侧主要负责 PUSH 调度,即业务侧决定触发和过滤,平台侧负责 PUSH 触达给用户终端。
由于 PUSH 是新闻增长护盘的重点方向,有较频繁的业务迭代,对底层消息通道我们有较多的业务需求,在业务迭代过程中我们发现平台侧需求 leadtime 比较长,无法满足业务侧迭代效率的要求;在经平台侧这边商量且同意后,我们完成新闻push消息通道的自研,直接对接厂商推送并搭建了长链接通道,实现了 push 全链路在业务侧的全闭环。
我们在自建 push 消息通道时,对原来的架构做了重写:
1)精简链路,模块整合,减少系统复杂度:去掉我们不关心的无用功能,将原链路15个模块,代码 68 万行整合为了6个模块,代码共8.6万行;通过代码精简能减少系统复杂度,有助于提升业务迭代效率;同时能避免模块之间的rpc通信开销,提升链路处理效率。
2)客户端/服务端交互接口整合,提升数据通信成功率:以前 PUSH 注册依赖于注册&绑定&上报三个接口请求,任何一次请求出错,push 注册就会失败;我们在新流程里将注册&绑定&上报需要的所有数据,都一起传给新接口,由服务端在一个接口里实现注册、绑定和上报;将注册成功率从90%提升到了99.9%。
3)与新闻技术技术架构保持统一:将原架构发现/rpc技术栈的基础组件升级为腾讯新闻自用的基础组件,尽量使用我们熟练使用的技术栈,以提升业务开发&运维效率。
4)优化了原来链路一些不合理的地方:对原来链路的限流机制、通道选择策略做了优化,增加了必要的功能,比如小流量实验环境的支持。
7、新Push架构优化2:统一技术栈

之前 push 链路有 C++/Golang 两种技术栈,除了 push 推荐服务外, 其它 C++链路模块全部使用 Golang 模块进行了重写,以提升业务迭代效率和链路稳定性。
8、新Push架构优化3:链路整合升级,提升效率

一个架构如果如果过度微服务化了,会带来各种问题:

  • 1)模块间耦合严重,影响研发效率:本来是一个模块应该完成的工作,硬拆成了2个模块,有改动需要都需要改两个模块,需要模块间联调测试,影响需求迭代效率。
  • 2)架构效率低:拆成微服务后,函数本地调用变成了RPC网络调用,需要增加大量的拆包、解包的操作,资源白白浪费在这些无用的内耗上了。
对于频繁迭代的地方,单独抽成单独的微服务是有助于提升迭代效率的;但是我们review历史push需求,都比较分散,没有集中到一个特定的地方,我们按照“一个需求尽量只用改一个模块”的原则,对原来的push链路的所有模块进行了整合升级。
具体的升级内容是:

  • a. 触发侧合并为了1个模块:将原来触发侧的5个模块合并为1个模块;
  • b. 调度侧合并为了1个模块:将原来调度侧的5个模块合并为了1个模块;
  • c. 将消息通道侧模块做了整合:如上所述,我们将push消息通道原来15个模块合并为了5个。
经过链路整合后:以前一个 PUSH 消息最多要经过 18 个模块,17次内部链路rpc转发;升级后,只用经过 3 个模块,只用经过 2 次 rpc 转发;这样就显著提升了链路效率;而且模块减少后,业务需要迭代无需开发多个模块,避免模块之间联调和测试,提升了业务迭代效率;同时,线上 case 排查时,无需做多模块的日志 join,提升了 case 排查效率。
9、新Push架构优化4:自建号码包服务,提升号码包获取速度

如上文所述:之前号码包的拉取慢是系统的主要瓶颈所在,而在我们这个场景比较简单,因此我们考虑自建号码包服务,针对于我们自己的需求来定制开发,以提升服务性能。我们的需求只有一个,就是对离线包进行分页,并提供服务接口返回指定页的数据。
1)画像中台圈选兴趣包,并按页切成若干个小文件,每个兴趣包一个文件夹,并上传到cos,兴趣包里带着数据版本号;
2)构建包管理服务,提供获取指定兴趣包指定页数据的能力;包管理服务定期从cos上check是否有更新的数据(比较本地数据版本和cos最新的数据版本),如果有,则拉取最新的数据更新本地数据;当接收到拉取指定包指定页数据的请求后,则定位到对应文件夹读取对应页文件数据并返回;
3)集群有个数据一致性哨兵,定期检查集群节点的数据版本,当发现集群数据版本不一致时,给集群所有节点发信号,强制让每个节点同步cos上的最新数据,让集群所有节点数据跟最新数据保持一致。
3.png
10、新Push架构优化5:在线过滤改成离线预处理,避免在线处理耗时

运营在发PUSH时会选择受众人群包,同时会指定系统、品牌等筛选项,之前的处理流程是先把人群包一股脑发到链路里,然后在下发链路里根据用户画像数据,对数据进行实时过滤。在线过滤增加了链路下发的耗时。
其实系统&品牌过滤完全可以前置到离线侧,我们将号码包按品牌和系统维度进行了拆分,比如“社会”包按 android/ios、huawei/oppo/vivo/honor/xiaomi,拆成了13个包,当运营选择指定的筛选项时,直接拉取对应的号码包,这样就避免了在线过滤的耗时,减少了下发的延时。
11、新Push架构优化6:将单IO操作自动聚合成批量操作

push下发链路有大量io操作,比如获取用户维度的多路数据(比如用户系统、品牌、下发&曝光&点击历史等),获取文章维度的多路数据(文章正排数据等)。链路其实主要耗时还是在io部分,如果能提升io吞吐量,就能提升PUSH链路的吞吐量,减少下发延时;io操作批处理肯定能提升吞吐量。
但是在具体业务流程中,不同push类型、不用品牌用户,处理逻辑会有不同,因为每个push的处理流程可能都不一样,无法直接批处理。所以之前调度主链路流程是从队列里按单个消费进行处理的。
为了提升链路吞吐量,我们对每一类io操作做了一个类,对外暴露一个单个io请求接口,外部调用该接口后,将请求压入一个异步队列,同时开始等待结果的返回;这样该类io请求都会在该异步队列里进行了汇聚。
下层会开若干个处理协程,批量从异步队列消费出若干请求任务,拼成批量的io请求,然后拿到批量io结果,按序向上层返回io结果;
这样对上层而言,看到的还是单个的同步io接口,上层业务逻辑开发流程无需做改造,底层其实已经自动做了io的批量聚合,显著提升了链路吞吐量。
12、新Push架构优化7:优先推送热点突发内容,优先保证高价值用户及时性体验

在链路吞吐量一定的情况下,一个推送任务小到几百万,大到一两亿的发送量,都需要处理时间。这时候先处理比后处理的时延要少。
其实可以考虑对链路发送进行调度:

  • 1)链路优先保障热点突发PUSH的发送,我们建立了任务优先级队列,当有热点突发PUSH在发送时,其它PUSH延迟发送;
  • 2)同一个PUSH任务,对用户推送顺序也做了排序:活跃度高、历史push点击率高、预估商业化价值高、对push时延敏感的用户优先发送。
通过优先级调度,最大程度保障了热点突发内容和高价值用户的推送及时性的体感。
13、新Push架构优化8:增加自动故障恢复能力

为了提升链路吞吐量,调度节点进程通过 LRU cache 缓存了大量数据,所以在推送消息处理的 sharding 方式上采用了按设备号一致性哈希。
很多时候某个节点异常时,会出现慢而不死的情况:处理能力陡降,但是节点存活正常。北极星未能把它摘掉,相当一部分设备会打到该节点,即使该节点已经满载了,之前架构为了避免缓存失效而导致处理耗时增加,还是会一致性哈希将流量打往该节点,导致这部分用户处理耗时异常增加,甚至发送失败。
新架构对于推送任务sharding做了优化:在一致性哈希的基础上,每个节点计算出4个固定的backup;当某节点的失败率或处理耗时超过一定阈值时,将该节点的流量均匀低分给他的backup。通过这种方式就支持单节点异常时的自动故障恢复。
14、新Push架构优化9:构建push链路自动化测试能力

构建了接口自动化回归测试流程:

  • 1)case覆盖push链路的核心逻辑;
  • 2)合并master时自动触发回归测试流程的执行。
构建了自动化diff测试流程:
diff流程大体思路都类似,通过录制线上流量的真实请求和返回结果,在测试环境进行回放,观察同一请求下,返回结果是否会有差别;如果无差别,说明测试环境跟线上一样,上线不会引起线上数据异常;如果有差别,就需要分析这些差别是否是符合预期的。
diff测试基本能回归到线上所有业务逻辑分支,能弥补回归测试覆盖度有限的问题。
主要挑战:
push依赖的数据变化比较快,导致在同一时间,同一请求的返回结果会不同;比如push为了避免重复下发同一篇文章,会依赖于下发历史数据,线上录制了刚下发的某篇文章,在测试环境去回放肯定就不能下发了,因为线上刚把这篇文章写入到下发历史里,导致回放请求时返回结果是不能下发了,这样自然就产生了diff。
解决方案:
在流量录制时,除了录制请求之外,同时录制各个依赖数据,在回放时,依赖数据以依赖数据为准,通过这种方案就避免了依赖数据易变而引入diff的问题。
15、架构升级后的系统表现

1)push运营成本显著降低:通过持续的 push 架构优化,新闻 push 总运营成本下降70%;
2)PUSH链路性能(吞吐量)显著提升:通过持续的 push 架构优化,显著提升了 push 链路的性能,push推送量(出口)峰值吞吐量提升了3.5倍;
3)热点突发(全国/快速)PUSH全链路耗时下降明显:

  • a. 热点突发(全国/快速)PUSH内部链路耗时P90下降了90%;
  • b. 内部链路耗时指的是从push审核通过到推送给厂商的时间,即我们内部链路总的耗时时长;
  • c. 热点突发(全国/快速)PUSH全链路耗时(包括内部链路耗时和厂商链路耗时)下降了90%
  • d. 全链路耗时指的是从push审核通过到用户收到PUSH时间,即包括内部链路和厂商链路总的耗时时长.
我们完成一些架构升级后,还是评测团队对了评测,腾讯新闻的PUSH已经领先于竞品1~4分钟了。
4)提升了PUSH点击效果:
push推送速度提升后,push点击数据也能看到明显受益,热点突发PUSH点击pv提升了10%,push大盘点击UV也能看到显著的正向收益;
线上收不到PUSH的用户客诉也减少到25年H1 0 例,提升了用户产品体验。
5)稳定性良好:push链路主要重构完成后,PUSH链路稳定性&质量明显提升,2025.02以后 0 故障。
16、参考资料

[1] 极光推送系统大规模高并发架构的技术实践分享
[2] 魅族2500万长连接的实时消息推送架构的技术实践分享
[3] 专访魅族架构师:海量长连接的实时消息推送系统的心得体会
[4] 一个基于长连接的安全可扩展的订阅/推送服务实现思路
[5] 实践分享:如何构建一套高可用的移动端消息推送系统?
[6] Go语言构建千万级在线的高并发消息推送系统实践(来自360公司)
[7] 腾讯信鸽技术分享:百亿级实时消息推送的实战经验
[8] 百万在线的美拍直播弹幕系统的实时推送技术实践之路
[9] 京东京麦商家开放平台的消息推送架构演进之路
[10] 技术干货:从零开始,教你设计一个百万级的消息推送系统
[11] 长连接网关技术专题(四):爱奇艺WebSocket实时推送网关技术实践
[12] 喜马拉雅亿级用户量的离线消息推送系统架构设计实践》
[13] 直播系统聊天技术(四):百度直播的海量用户实时消息系统架构演进实践
[14] 消息推送技术干货:美团实时消息推送服务的技术演进之路
[15] 揭秘vivo百亿级厂商消息推送平台的高可用技术实践
[16] 得物从零构建亿级消息推送系统的送达稳定性监控体系技术实践
[17] B站千万级长连接实时消息系统的架构设计与实践
[18] 转转千万级用户量消息推送系统的架构演进之路
[19] 企业级实时消息推送系统的架构设计,一文即懂!
即时通讯技术学习:
- 移动端IM开发入门文章:《新手入门一篇就够:从零开发移动端IM》
- 开源IM框架源码:https://github.com/JackJiang2011/MobileIMSDK(备用地址点此)
(本文已同步发布于:http://www.52im.net/thread-4883-1-1.html)

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册