Jack Jiang

我的最新工程MobileIMSDK:http://git.oschina.net/jackjiang/MobileIMSDK
posts - 523, comments - 13, trackbacks - 0, articles - 1

2025年10月30日

本文由腾讯技术团队颜勇分享,原题“腾讯新闻PUSH架构升级之路”,有修订和重新排版。

1、引言

68 万行代码精简到8.6 万;Golang 重写大部分 C++模块;解决过度微服务化问题…… 这是新闻 PUSH 架构团队取得的技术收益。

PUSH 是腾讯新闻精品资讯的重要分发途径,也是新闻 App 重要的促活手段。作为 PUSH 架构团队,我们一方面在积极支持好新闻护盘,同时也在对 PUSH 架构进行不断的升级与进化,以持续提升 PUSH 系统的稳定性与质量、研发效率,同时持续减少运营成本。

本文主要分享的是腾讯技术团队近年来对腾讯新闻消息推送PUSH系统做的架构优化和技术实践。

cover-opti

2、Push平台介绍

2.1 概述

PUSH 是腾讯新闻内容重要的分发渠道,新闻 PUSH 平台承担着将新闻资讯触达到新闻用户、满足用户及时获取精品资讯的需求。

1

总体上,新闻 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上的最新数据,让集群所有节点数据跟最新数据保持一致。

2

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

posted @ 2025-12-08 19:12 Jack Jiang 阅读(21) | 评论 (0)编辑 收藏

本文由自字节跳动技术肖新蔚、赵彦奇分享,有修订和重新排版。

1、引言

本文要分享的是字节跳动团队针对火山HTTPDNS Cache2.0通过自研网段库与动态划分算法,将缓存粒度从“城市-运营商”细化为“网段”,解决了传统方案的城市级调度污染问题。配合缓存分级、预取等优化,在提升调度精准度的同时保证了高命中率,最终实现了服务端调度准确性提升和客户端性能优化。

cover-opti

技术交流:

- 移动端IM开发入门文章:《新手入门一篇就够:从零开发移动端IM

- 开源IM框架源码:https://github.com/JackJiang2011/MobileIMSDK备用地址点此

(本文已同步发布于:http://www.52im.net/thread-4876-1-1.html

2、系列文章

移动端弱网优化专题(一):通俗易懂,理解移动网络的“弱”和“慢”

移动端弱网优化专题(二):史上最全移动弱网络优化方法总结

移动端弱网优化专题(三):现代移动端网络短连接的优化手段总结

移动端弱网优化专题(四):百度APP网络深度优化实践(DNS优化篇)

移动端弱网优化专题(五):百度APP网络深度优化实践(网络连接优化篇)

移动端弱网优化专题(六):百度APP网络深度优化实践(移动弱网优化篇)

移动端弱网优化专题(七):爱奇艺APP网络优化实践(网络请求成功率优化篇)

移动端弱网优化专题(八):美团点评的网络优化实践(大幅提升连接成功率、速度等)

移动端弱网优化专题(九):淘宝移动端统一网络库的架构演进和弱网优化实践

移动端弱网优化专题(十):爱奇艺APP跨国弱网通信的优化实践

移动端弱网优化专题(十一):美图APP的移动端DNS优化实践

移动端弱网优化专题(十二):得物自研移动端弱网诊断工具的技术实践

移动端弱网优化专题(十三):得物移动端常见白屏问题优化(网络优化篇)

移动端弱网优化专题(十四):携程APP移动网络优化实践(弱网识别篇)

移动端弱网优化专题(十五):字节跳动移动端网络HttpDNS优化实践》(☜ 本文

3、技术背景

在字节跳动的业务生态中,HTTPDNS 承担着为抖音、今日头条、西瓜视频等核心应用提供域名解析服务的重任。但目前我们所采用的业界主流缓存机制(火山Cache1.0),却存在着调度不准的问题。

这些问题主要是:

  • 1)业界主流缓存机制的问题;
  • 2)缓存粒度:城市-运营商;
  • 3)致命缺陷:当自身IP库与权威DNS服务器不同,易发生调度不准,可能影响用户体验。

4、主流HttpDNS调度修正机制的局限性

针对 HTTPDNS 调度不准风险,业界主流处置流程采用 “发现-定位-修复” 三步闭环机制.

具体如下:

  • 1)发现:通过监控告警、业务异常反馈等方式,识别存在调度偏差的解析场景;
  • 2)定位:结合访问日志、链路追踪数据等,定位调度不准的具体域名、源IP段和目标 IP 段;
  • 3)修复:通过技术手段修正解析结果。

针对上述第 3)点,核心修复方式包含以下两类(均存在显著局限性):

  • 1)地址库升级:基于外部供应商数据聚合构建的 IP 地址库,即使实时更新,仍难与外部 CDN 厂商的映射保持一致;
  • 2)临时劫持:手动配置解析劫持规则修正解析结果,不仅操作流程繁琐、耗时长,且需人工维护大量静态配置;若规则未得到及时维护,易引发解析结果异常。 
1

5、主流厂商的HttpDNS缓存粒度技术方案

缓存粒度设计直接影响 DNS 解析精准度,主流厂商的方案存在明显差异:

2

6、HttpDNS的缓存键精细化重构

我们综合考量调度精准度、工程复杂度以及成本,决定将缓存粒度由“城市+运营商”细化为“网段”。

6.1 传统方案(国内某厂商/火山Cache1.0)

3

  • 1)缓存粒度:城市+运营商;
  • 2)污染范围:整个城市运营商;
  • 3)调度准确性:低。

6.2 Cache2.0方案

4
  • 1)缓存粒度:网段;
  • 2)污染范围:单个网段;
  • 3)调度准确性:高。

6.3 网段自适应划分算法

背景:外部 CDN 厂商的调度结果会随网络拓扑和调度策略持续变化,而静态网段库划分方式固定,难以实时跟踪调度结果变化。

为解决这一问题,网段库动态划分算法通过“数据输入—一致性校验—网段调整—结果输出”的闭环流程,实现了网段库的自适应动态划分。

具体流程如下。

1)数据输入:

* 收集客户端IP—CDN IP映射数据:

  • a)数据来源:主动拨测结果;HTTPDNS 递归节点日志;
  • b)数据范围:主流CDN厂商的解析结果。

* 网段归属判断:

  • a)若相邻客户端IP的CDN IP 归属同一运营商,则该组CIP可合并为连续网段;
  • b)将合并后的连续网段输出,作为探测网段数据集。

2)一致性校验:

  • a)将探测网段数据集与存量CIDRDB网段库进行逐网段对比,检查 “映射一致性”;
  • b)若存在映射不一致,则触发网段调整流程。

3)网段调整:

  • a)合并:探测数据集的网段比现有库粗,合并为大网段;
  • b)拆分:探测数据集的网段比现有库细,拆分为小网段。

4)结果输出:

  • a)生成优化后的新CIDRDB网段库;
  • b)替换存量网段库,实现动态更新。

5)持续迭代:

  • a)重复上述流程,实现网段库的自适应动态划分。
5

7、HttpDNS的缓存策略优化

为解决缓存粒度细化可能导致的命中率下降问题,Cache2.0 引入了四重优化策略,最终实现了如下收益:

缓存命中率提高了15%,缓存量、CPU 使用和出网流量降低了约70%。

1)两级一致性哈希分流:

火山 HTTPDNS 的流量转发以一致性哈希思想为核心,将用户请求链路(用户→LB→缓存层→递归层)拆分为两级哈希调度:

  • a)一级调度(LB→缓存层):以“源 IP + 域名”为哈希键。使用LB的一致性哈希策略,将同一用户对同一域名的请求统一路由至固定的 HTTPDNS 节点,避免传统轮询导致的请求分散;
  • b)二级调度(缓存层→递归层):以“域名 + 网段” 为哈希键。以 “域名 + 客户端网段” 作为哈希键,与缓存粒度完全对齐,确保某一“域名 + 网段”对应的查询请求均定向到唯一的递归层节点。

两级哈希协同调度,解决了缓存的碎片化问题,同时单一节点故障影响范围极小。

6

 


 

2)缓存分级管理:

在 HTTPDNS 场景中,不同域名对解析精度的需求不同。高优先级域名(如API 调用、直播 / 点播流媒体分发)对解析精准性要求高,跨网可能导致访问延迟增加;而低精度需求域名(如302域名)采用过细缓存会浪费存储资源,频繁回源也会增加权威 DNS 压力。

为实现缓存资源的精细化分配,火山 HTTPDNS 将缓存体系划分为“网段缓存、城市 - 运营商缓存、全局缓存” 三级,各级缓存适配不同应用场景。

具体是:

1)网段缓存:作为最高精度层级,聚焦高优先级业务场景 :一方面适配高优域名(如抖音 API 调用、图片分发、点播 / 直播流媒体传输等对精准性敏感的域名),另一方面服务重点集群(如 ToB 企业 HTTPDNS 服务、ToB 专属公共 DNS 服务),通过网段级细粒度缓存确保解析结果与用户实际网络链路高度匹配,降低访问延迟;

2)城市 - 运营商缓存:定位中等精度层级,适配普通域名场景:针对调度精准度要求较低的域名,以 “城市 + 运营商” 为缓存单元,平衡缓存命中率与存储开销;

3)全局缓存:作为基础精度层级,专门适配非智能解析域名:针对不支持 CDN 动态调度、解析结果无地域 / 运营商差异的域名(如静态官网、通用工具类服务域名),采用全局统一缓存策略,所有用户查询共享同一缓存结果,最大化提升缓存命中率,降低回源请求压力。

7

3)缓存更新分级策略:

在 HTTPDNS 系统中,统一的主动刷新策略虽然能保证缓存命中率,但存在明显问题:对不需要精细调度的域名浪费了存储资源,增加了下游压力。

基于以上问题,火山 HTTPDNS引入 “主动刷新 + 被动刷新”分级策略,以域名优先级和业务需求为依据,将缓存更新机制分为两类。

具体是:

  • a)后台线程主动刷新机制:针对高优域名(白名单),保留后台线程主动刷新,确保缓存持续有效、用户请求直接命中最新数据;
  • b)用户请求被动刷新机制:针对普通域名或非智能解析域名,由请求触发缓存更新,按需刷新,无需常驻后台刷新线程,降低资源消耗。

通过这种分级更新策略,高优先级域名仍能保证低延迟和高命中率,同时普通域名的刷新开销显著降低。

4)缓存预取机制:

依托 “缓存空间局部性原理”,火山 HTTPDNS 设计了缓存预取机制。当某条缓存请求(如 A 网段域名解析)触发更新时,系统不仅刷新目标网段缓存,还会同步更新与其具有 “亲缘关系” 的网段缓存(“亲缘关系”指地理相邻、同运营商节点覆盖的网段)。这种 “单次请求触发批量预取” 的设计能够提前将关联网段缓存置于准备状态,提升后续请求的命中率。

以抖音直播域名的实际访问场景为例,预取机制的运作过程如下:

  • a)本网段更新:当用户 A(IP 归属北京联通 10.0.1.0/24 网段)发起直播域名解析请求时,系统首先刷新其所属的 10.0.1.0/24 网段缓存;
  • b)预取更新:系统同时刷新与 10.0.1.0/24 网段具有亲缘关系的网段缓存,例如北京联通下的相邻网段(10.0.2.0/24、10.0.3.0/24),确保这些网段缓存也处于准备状态。

随后,当用户 B(10.0.2.0/24)或用户 C(10.0.10.0/24)发起相同直播域名的解析请求时,由于对应网段缓存已提前预取,无需等待回源即可直接命中缓存,显著降低访问延迟。

8、HttpDNS优化后的实际效果

8

服务端调度精准度提高:借助网段级缓存,用户获取的 IP 地址更加精准。按服务端日志数据口径,调度不准比例从万分之六下降至万分之二,降幅 60%,有效缓解了传统粗粒度缓存导致的“城市级缓存污染”问题。

客户端性能优化:

  • 1)成功率:核心 feed 接口,在弱网+非连接复用场景下提升 1.15%;
  • 2)耗时:非连接复用场景耗时减少14ms。
9

用户体验提升:

  • 1)性能指标:首刷及启动耗时下降;
  • 2)用户指标:用户行为指标(send 与 click)正向,用户活跃度提升。

本方案通过服务端精准调度 → 客户端性能优化 → 用户体验提升,实现了全链路效能提升。

9、持续演进方向——共享缓存

目前,各机房的负载均衡策略与缓存策略未能完全对齐(部分采用随机转发,部分虽然使用一致性哈希但粒度不一致),导致同一数据在多个实例中被重复缓存,资源利用率偏低,缓存命中率也有待提升。

未来,我们计划构建一个分层共享的高可用缓存体系:

1)在同一机房内,实例通过一致性哈希协同分工,每台实例既是分片缓存,也能代理转发请求,从而减少重复存储并提升命中率;

2)在跨机房层面,按区域部署二级缓存节点,作为容量更大、延迟更低的共享中心,承接一级未命中的请求,降低跨区域访问和上游压力。与此同时,引入热点数据副本、请求合并和故障转移等机制,保证高并发和异常情况下的稳定性与可用性。

通过这一演进,整体架构将逐步升级为层次化、分布式且具备高可用能力的缓存网络,为业务的持续扩展提供坚实支撑。

10、参考资料

[1] TCP/IP详解 卷1:协议 - 第14章 DNS:域名系统

[2] 网络编程懒人入门(七):深入浅出,全面理解HTTP协议

[3] 网络编程懒人入门(十二):快速读懂Http/3协议,一篇就够!

[4] 从HTTP/0.9到HTTP/2:一文读懂HTTP协议的历史演变和设计思路

[5] 脑残式网络编程入门(三):HTTP协议必知必会的一些知识

[6] 全面了解移动端DNS域名劫持等杂症:原理、根源、HttpDNS解决方案等

[7] 通俗易懂,理解移动网络的“弱”和“慢”

[8] 现代移动端网络短连接的优化手段总结

[9] 百度APP网络深度优化实践(DNS优化篇)

[10] 爱奇艺APP网络优化实践(网络请求成功率优化篇)

[11] 美团点评的网络优化实践(大幅提升连接成功率、速度等)

[12] 淘宝移动端统一网络库的架构演进和弱网优化实践

[13] 爱奇艺APP跨国弱网通信的优化实践

[14] 得物自研移动端弱网诊断工具的技术实践

[15] 携程APP移动网络优化实践(弱网识别篇)

(本文同步发布于:http://www.52im.net/thread-4876-1-1.html

posted @ 2025-11-25 10:45 Jack Jiang 阅读(31) | 评论 (0)编辑 收藏

本文来自转转技术李帅的原创分享,已进行修订和排版优化。

1、引言

在当今互联网时代,高效的用户服务是提升用户体验的关键。转转自研的客服IM聊天系统作为用户与客服沟通的桥梁,承担着传递信息、解决问题的关键角色。然而,消息数据的流转并非一帆风顺,本文将深入探讨IM系统在消息传递过程中遇到的问题和挑战,以及相应的技术解决方案。

如图是IM系统中一条消息的流转链路:

 1

相较于普通web系统,IM系统的消息数据流转链路更长、更复杂。从客户端到服务端,再从服务端到另一个客户端,任何一个环节的故障都可能导致消息延迟、丢失、乱序或重复,从而影响用户体验。

网络波动和客户端设备性能的不稳定性是影响IM系统性能的主要因素,这些因素可能导致消息的实时性、可靠性和完整性受到威胁。

技术交流:

- 移动端IM开发入门文章:《新手入门一篇就够:从零开发移动端IM

- 开源IM框架源码:https://github.com/JackJiang2011/MobileIMSDK备用地址点此

(本文已同步发布于:http://www.52im.net/thread-4874-1-1.html

2、关于作者

李帅:转转履约中台研发工程师,主要负责客服工单、IM等系统研发。

3、如何保证聊天消息的实时性

首当其冲的就是消息延迟问题:当一条消息发出后,我们的系统需要确保这条消息最快被接收人感知并获取到,并且保证资源消耗较少。

这里关键的点是:

  • 1)最快触达;
  • 2)耗费资源少。

方案1:长短轮询

在PC web早期,大部分应用都是采用“一问一答”的请求响应模式来获取数据,IM系统采用客户端轮询的方式,定期、高频轮询获取服务端的新消息。这种方式开发成本较低、容易实现,但是高频轮询很多请求是无用请求,客户端浪费流量和电量,服务端资源压力很大。

后来基于短轮询进化出长轮询模式,相较于前者,后者在请求时获取到新数据时不会立即返回,而是在服务端保持连接等待一段时间,如果等待期间有新消息就立即返回响应,长轮询仅仅解决了客户端的无用消耗,但是服务端资源高负载情况依然未能解决。

方案2:WebSocket

随着HTML5的出现,全双工的WebSocket成为解决这一问题的关键。基于WebSocket实现的IM服务,客户端和服务端只需要完成一次握手,就可以创建持久的长连接,并进行随时的双向数据传输。

2
经过比较转转客服IM系统采用了WebSocket协议,具体使用方式见《转转客服IM系统的WebSocket集群架构设计和部署方案》。当服务端接收到新消息时,可以通过建立的WebSocket连接,直接进行推送,保证了消息到达的实时性。

关于传统Web和现代Web的实时通信技术,可详读以下几篇:

  1. Web端即时通讯技术盘点:短轮询、Comet、Websocket、SSE
  2. 详解Web端通信方式的演进:从Ajax、JSONP 到 SSE、Websocket
  3. 网页端IM通信技术快速入门:短轮询、长轮询、SSE、WebSocket

4、什么是聊天消息的可靠性

如图是一条消息发送的核心步骤,整个过程中可以分为两个部分,消息由客户端发送到服务端(第1、2步),服务端将消息推送至另一个客户端(第3步),发送过程中任何一步出现问题都会导致消息发送失败。

3

PS:限于篇幅原因,本文不想深入探讨,有兴趣可详读《零基础IM开发入门(三):什么是IM系统的可靠性?》。

5、如何保证聊天消息触必达?

我们参考使用了TCP/IP协议中的ACK机制来实现防丢逻辑(《TCP/IP详解 - 第17章·TCP:传输控制协议》)。ACK机制是TCP/IP协议三次握手重要的一环(请详读《脑残式网络编程入门(一):跟着动画来学TCP三次握手和四次挥手》),用于确认对方发送信息无误。

ACK响应机制如下:

  • 1)发送者发送消息时会携带一个消息标识符(此处使用发送方id和消息发送时间戳)、并在本地维护一个“等待ACK消息列表”;
  • 2)接收者收到消息后对消息进行存储得到消息id;
  • 3)随后再将该标识回传给发送方(ACK消息);
  • 4)发送方收到ACK消息后将消息从“等待ACK消息列表”删除;
  • 5)当发送方没有在约定时间内收到ACK消息时,就需要执行失败消息处理逻辑:自动重发、客户端标记发送失败等。

服务端实现与客户端稍有不同,服务端需要要维护全量用户的消息,使用定时任务检查等待ACK消息列表效率比较低,此处通过mq的延迟消息来实现:

当消息发出时同时发送一个延迟mq,延迟消息被消费时对应的消息仍在等待ACK列表中,则表示消息未能在规定时间内被确认,需要进行重试发送。

如图为完整的ACK实现机制:

4

另外客户端也会在页面刷新、WebSocket重连时触发http接口重新拉取当前会话的所有消息进行渲染,保证消息不丢失。

PS:相关资料可进一步阅读:

  1. IM消息送达保证机制实现(一):保证在线实时消息的可靠投递
  2. 阿里IM技术分享(四):闲鱼亿级IM消息系统的可靠投递优化实践
  3. 融云技术分享:全面揭秘亿级IM消息的可靠投递机制
  4. 一套亿级用户的IM架构技术干货(下篇):可靠性、有序性、弱网优化等

6、如何对聊天消息去重?

消息重推解决了消息丢失的问题,但是因为ACK消息本身就可能会丢失从而导致消息重复,因此我们需要保证推送消息和重推消息有相同且唯一的消息id,接收方可以根据该消息id进行数据去重。

具体是:

  • 1)发送方:客户端使用发送人id和消息发送时间戳作为唯一的ACK标识,传递给服务端;
  • 2)服务端:使用雪花算法接收到的消息生成消息id,将ACK标识与消息id建立映射关系;服务端再将消息id推送至发送方和接收方。

一个完整的消息发送流程如图所示:

5

7、心跳保活机制

IM系统中接收和发送消息都是使用长连接实现,但是如果连接断开,那发送和接收数据就会出现问题。在客服业务中,人工客服席位有限,系统需要可靠的机制保证人工客服资源有效利用。

为此我们在应用层设计心跳消息,使用该机制更新用户当前状态、保证会话有序退出。

心跳机制设计如下。

客户端:设置定时器,用户建立连接后,定时发送(30s)心跳消息给服务端。

服务端:

  • 1)接收心跳消息,更新心跳时间;
  • 2)设置定时任务,定时扫描在线用户上次心跳时间,执行以下逻辑;
  • 3)上次心跳时间超出30s,将其标记为离线状态,关闭连接,等待用户重连;
  • 4)上次心跳时间超出2分钟则认为用户已经彻底离开,执行会话关闭逻辑释放人工客服资源。

应用层心跳消息仅用于保活和状态更新,因此数据结构设计十分精简,不携带额外信息。

关于心跳保活这方面的资料,可以进一步阅读:

  1. 为何基于TCP协议的移动端IM仍然需要心跳保活机制?
  2. 一文读懂即时通讯应用中的网络心跳包机制:作用、原理、实现思路等
  3. 微信团队原创分享:Android版微信后台保活实战分享(网络保活篇)
  4. 融云技术分享:融云安卓端IM产品的网络链路保活技术实践
  5. 跟着源码学IM(五):正确理解IM长连接、心跳及重连机制,并动手实现
  6. 阿里IM技术分享(五):闲鱼亿级IM消息系统的及时性优化实践
  7. 万字长文:手把手教你实现一套高效的IM长连接自适应心跳保活机制

8、消息协议的设计

在IM系统中消息格式的设计也十分重要,良好的数据格式可以准确传递消息内容并具有极高的可读性。

我们根据消息类型和发送流向将消息数据格式大致分为如下几部分:

  • 1)消息类型:用于描述消息的用途、流向,如心跳消息、用户/客服消息、系统消息等;
  • 2)客服id:接收或者发送消息的客服标识;
  • 3)用户id:接收或者发送消息的用户标识;
  • 4)消息内容:实际的消息,与消息类型相关;
  • 5)消息格式:用于描述用户/客服消息格式,如文本、图片、视频、订单卡片、优惠券等;
  • 6)消息文本:消息的展示内容。
6

PS:IM协议设计相关资料可进一步阅读:

  1. 简述移动端IM开发的那些坑:架构设计、通信协议和客户端
  2. 理论联系实际:一套典型的IM通信协议设计详解
  3. APP与后台通信数据格式的演进:从文本协议到二进制协议
  4. IM通讯协议专题学习(一):Protobuf从入门到精通,一篇就够!

9、本文小结

转转客服IM系统通过引入WebSocket协议、ACK机制、消息重推和数据去重等策略,有效应保障了消息传递过程中的实时性、可靠性和完整性。这些技术的应用,不仅提升了用户与客服之间的沟通效率,也为转转平台提供了更加稳定、高效的服务支持。

在未来的发展中,我们将继续优化和完善,以应对不断变化的需求和用户期望,为用户提供更加优质的服务体验。

10、参考资料

[0] TCP/IP详解 - 第17章·TCP:传输控制协议

[1] Web端即时通讯技术盘点:短轮询、Comet、Websocket、SSE

[2] 详解Web端通信方式的演进:从Ajax、JSONP 到 SSE、Websocket

[3] 网页端IM通信技术快速入门:短轮询、长轮询、SSE、WebSocket

[4] Web端即时通讯实践干货:如何让你的WebSocket断网重连更快速?

[5] 简述移动端IM开发的那些坑:架构设计、通信协议和客户端

[6] IM消息送达保证机制实现(一):保证在线实时消息的可靠投递

[7] 阿里IM技术分享(四):闲鱼亿级IM消息系统的可靠投递优化实践

[8] 阿里IM技术分享(五):闲鱼亿级IM消息系统的及时性优化实践

[9] 融云技术分享:全面揭秘亿级IM消息的可靠投递机制

[10] 一套亿级用户的IM架构技术干货(下篇):可靠性、有序性、弱网优化等

[11] 理解IM消息“可靠性”和“一致性”问题,以及解决方案探讨

[12] 微信团队分享:来看看微信十年前的IM消息收发架构,你做到了吗

[13] 阿里IM技术分享(七):闲鱼IM的在线、离线聊天数据同步机制优化实践

[14] IM群聊消息如此复杂,如何保证不丢不重?

[15] 开发IM是自己设计协议用字节流好还是字符流好?

[16] 零基础IM开发入门(一):什么是IM系统?

[17] 零基础IM开发入门(二):什么是IM系统的实时性?

[18] 零基础IM开发入门(三):什么是IM系统的可靠性?

[19] 零基础IM开发入门(四):什么是IM系统的消息时序一致性?

[20] 脑残式网络编程入门(一):跟着动画来学TCP三次握手和四次挥手

本文已同步发布于:http://www.52im.net/thread-4874-1-1.html

posted @ 2025-11-11 19:49 Jack Jiang 阅读(50) | 评论 (0)编辑 收藏

1、前言

经常发现有开发者将密钥硬编码在Java代码、文件中,这样做会引起很大风险。

信息安全的基础在于密码学,而常用的密码学算法都是公开的,加密内容的保密依靠的是密钥的保密,密钥如果泄露,对于对称密码算法,根据用到的密钥算法和加密后的密文,很容易得到加密前的明文;对于非对称密码算法或者签名算法,根据密钥和要加密的明文,很容易获得计算出签名值,从而伪造签名。

密钥硬编码在代码中,而根据密钥的用途不同,这导致了不同的安全风险,有的导致加密数据被破解,数据不再保密,有的导致和服务器通信的加签被破解,引发各种血案,本文主要借用乌云上已公布的几个APP漏洞来讲讲这其中的潜在风险和危害。

技术交流:

- 移动端IM开发入门文章:《新手入门一篇就够:从零开发移动端IM

(本文已同步发布于:http://www.52im.net/thread-312-1-1.html)

2、风险案例(一):某互联网金融APP加密算法被破解导致敏感信息泄露

某P2P应用客户端,用来加密数据的DES算法的密钥硬编码在Java代码中,而DES算法是对称密码算法,既加密密钥和解密密钥相同。

反编译APP,发现DES算法:

1

发现DES算法的密钥,硬编码为“yrdAppKe”,用来加密手势密码:

2

将手势密码用DES加密后存放在本地LocusPassWordView.xml文件中:

3

知道了密文和加密算法以及密钥,通过解密操作,可以从文件中恢复出原始的手势密码。或者使用新的生成新的手势密码,而与服务器通信时接口中的Jason字段也用了DES算法和密钥硬编码为“yRdappKY”:

4

5

和服务器通信采用http传输,没有使用https来加密通信,如果采用中间人攻击或者路由器镜像,获得流量数据,可以破解出用户的通信内容。

3、风险案例(二):某租车APP加密算法被破解导致一些列风险

某租车APP与服务器通信的接口采用http传输数据,并且有对传输的部分参数进行了加密,加密算法采用AES,但是密钥硬编码在java代码中为“shenzhoucar123123”,可被逆向分析出来,导致伪造请求,结合服务器端的漏洞,引起越权访问的风险,如越权查看其它用户的订单等。

和服务器通信时的数据为:

6

q字段是加密后的内容。逆向APP,从登录Activity入手:

7

分析登录流程:

v1是用户名,v2是密码,v3是PushId,在用户名和密码不为空并且长度不小于11情况下,执行LoginOperate相关操作,追踪LoginOperate的实现,发现继承自BaseOperate,继续追踪BaseOperate的实现。

8

在BaseOperate的initUrl()方法中,找到了APP是怎么生成请求数据的:

9

继续追踪上图中的initJsonUrl()方法,发现其调用了AES加密:

10

继续追踪aes.onEncrypt()函数:

11

在onEncrypt()函数中调用了encrypt()函数用来加密数据,追踪encrypt()函数的实现:

发现其使用AES算法,并且密钥硬编码在java代码中为“shenzhoucar123123”。

12

构造{“id”:”11468061”}的请求:

到现在请求中的数据加密如何实现的就清晰了,另外由于服务器权限控制不严,就可以构造订单id的请求,达到越权访问到其他用户的订单。

13

其中uid设置为你自己的uid即可,可以成功看到其他人的订单:

14

攻击者完全可以做到使用其他脚本重新实现相同的加密功能并拼接出各个接口请求,批量的刷取订单信息和用户其他信息。

4、风险案例(三):某酒店APP加签算法被破解导致一系列风险

某酒店APP和服务器通信时接口采用http通信,数据进行了加密,并且对传输参数进行签名,在服务器端校验签名,以检查传输的数据是否被篡改,但是加签算法和密钥被逆向分析,可导致加签机制失效,攻击者可任意伪造请求包,若结合服务器端的权限控制有漏洞,则可引发越权风险等。

APP和服务器通信的原始包如下图,可以看到有加签字段sign:

15

逆向APP定位到加密算法的逻辑代码,com.htinns.biz.HttpUtils.class,其实现逻辑为:

16

原始数据是unSignData,使用RC4算法加密,密钥为KEY变量所代表的值,加密后的数据为signData,传输的数据时的data字段为signData。 加签字段signd的生成方法是用unsignData拼接时间戳time和resultkey,然后做md5,再进行base64编码。时间戳保证了每次请求包都不一样。 sendSign()算法是用c或c++写的,放入了so库,其他重要算法都是用java写的。

可以使用IDA逆向分析so库,找到sendSign()方法:

17

而乌云漏洞提交者采用的是分析sign和getSign(sign)的数据,做一个算法破解字典。其实还有种方法直接调用此so库,来生成字典。签名破解以后,就可以构造发送给服务器的数据包进行其他方面的安全测试,比如越权、重置密码等。

5、总结及建议

通过以上案例,并总结下自己平时发现密钥硬编码的主要形式有:

  • 1)密钥直接明文存在sharedprefs文件中,这是最不安全的。
  • 2)密钥直接硬编码在Java代码中,这很不安全,dex文件很容易被逆向成java代码。
  • 3)将密钥分成不同的几段,有的存储在文件中、有的存储在代码中,最后将他们拼接起来,可以将整个操作写的很复杂,这因为还是在java层,逆向者只要花点时间,也很容易被逆向。
  • 4)用ndk开发,将密钥放在so文件,加密解密操作都在so文件里,这从一定程度上提高了的安全性,挡住了一些逆向者,但是有经验的逆向者还是会使用IDA破解的。
  • 5)在so文件中不存储密钥,so文件中对密钥进行加解密操作,将密钥加密后的密钥命名为其他普通文件,存放在assets目录下或者其他目录下,接着在so文件里面添加无关代码(花指令),虽然可以增加静态分析难度,但是可以使用动态调式的方法,追踪加密解密函数,也可以查找到密钥内容。

保证密钥的安全确是件难事,涉及到密钥分发,存储,失效回收,APP防反编译和防调试,还有风险评估。可以说在设备上安全存储密钥这个基本无解,只能选择增大攻击者的逆向成本,让攻击者知难而退。而要是普通开发者的话,做妥善保护密钥这些事情这需要耗费很大的心血。

产品设计者或者开发者要明白自己的密钥是做什么用的,重要程度怎么样,密钥被逆向出来会造成什么风险,通过评估APP应用的重要程度来选择相应的技术方案。

6、参考资料

[1] https://www.zhihu.com/question/35136485/answer/84491440

[2] 传输层安全协议SSL/TLS的Java平台实现简介和Demo演示

[3] 理论联系实际:一套典型的IM通信协议设计详解(含安全层设计)

[4] 微信新一代通信安全解决方案:基于TLS1.3的MMTLS详解

[5] 来自阿里OpenIM:打造安全可靠即时通讯服务的技术实践分享

[6] 简述实时音视频聊天中端到端加密(E2EE)的工作原理

[7] 移动端安全通信的利器——端到端加密(E2EE)技术详解

[8] Web端即时通讯安全:跨站点WebSocket劫持漏洞详解(含示例代码)

[9] 通俗易懂:一篇掌握即时通讯的消息传输安全原理

[10] IM开发基础知识补课(四):正确理解HTTP短连接中的Cookie、Session和Token

[11] 快速读懂量子通信、量子加密技术

[12] 一分钟理解 HTTPS 到底解决了什么问题

[13] 一篇读懂HTTPS:加密原理、安全逻辑、数字证书等

[14] 基于Netty的IM聊天加密技术学习:一文理清常见的加密概念、术语等

[15] 手把手教你为基于Netty的IM生成自签名SSL/TLS证书

[16] 微信技术分享:揭秘微信后台安全特征数据仓库的架构设计

[17] 即时通讯初学者必知必会的20个网络编程和通信安全知识点

[18] 零基础IM开发入门(五):什么是IM系统的端到端加密?

7、IM安全系列文章

本文是IM通讯安全知识系列文章中的第 3 篇,总目录如下:

即时通讯安全篇(一):正确地理解和使用Android端加密算法

即时通讯安全篇(二):探讨组合加密算法在IM中的应用

即时通讯安全篇(三):常用加解密算法与通讯安全讲解

即时通讯安全篇(四):实例分析Android中密钥硬编码的风险》(☜ 本文)

即时通讯安全篇(五):对称加密技术在Android上的应用实践

即时通讯安全篇(六):非对称加密技术的原理与应用实践

即时通讯安全篇(七):用JWT技术解决IM系统Socket长连接的身份认证痛点

即时通讯安全篇(八):如果这样来理解HTTPS原理,一篇就够了

即时通讯安全篇(九):你知道,HTTPS用的是对称加密还是非对称加密?

即时通讯安全篇(十):为什么要用HTTPS?深入浅出,探密短连接的安全性

即时通讯安全篇(十一):IM聊天系统安全手段之通信连接层加密技术

即时通讯安全篇(十二):IM聊天系统安全手段之传输内容端到端加密技术

即时通讯安全篇(十三):信创必学,一文读懂什么是国密算法

即时通讯安全篇(十四):网络端口的安全防护技术实践

即时通讯安全篇(十五):详解硬编码密码的泄漏风险及其扫描原理和工具


(本文已同步发布于:http://www.52im.net/thread-312-1-1.html)

posted @ 2025-10-30 11:24 Jack Jiang 阅读(42) | 评论 (0)编辑 收藏

Jack Jiang的 Mail: jb2011@163.com, 联系QQ: 413980957, 微信: hellojackjiang