写点什么

得物 App 白屏优化系列|网络篇

作者:得物技术
  • 2024-08-22
    上海
  • 本文字数:7620 字

    阅读完需:约 25 分钟

得物App白屏优化系列|网络篇

一、背景


图片加载作为重中之重的 App 体验指标,端侧的白屏问题则是其中最为严重的问题之一。想象一下如果你在浏览交易商品、社区帖子等核心场景下,图片无法完成加载是多么糟糕的体验。


网络作为图片资源加载的最主要来源途径,如果不能够快速的响应请求,那对上层图片库而言,就是巧妇难为无米之炊了。


而且,通过线上白屏问题归因,我们看到网络问题导致比例最高,占比达 81.97%。除去常见的弱网/无网等问题外,还有很多各种各样的网络环境问题我们是可以进行优化的,例如设备不支持 IPv6,CDN 节点异常,证书超时等。


二、网络优化 &监控概览


网络优化与监控体系

三、网络监控能力


网络异常导致的图片白屏问题,往往和当时的环境有关,例如用户连的 WIFI 不支持 IPv6 但是 DNS 返回的大部分是 V6 的 IP 等,此时仅靠几条带有 SocketTimeoutException 的网络日志根本无法排查出问题的根因,就如同 ANR 问题需要火焰图一样,白屏问题同样需要一套完善的监控体系来记录问题现场的信息,从而分析问题根因。

OkHttp 基础监控

图片网络请求的阶段信息,可以通过实现 OkHttp 自带的 EventListener 来获取,在各个阶段开始和结束点记录下时间戳即可。


其中需要重点关注 connectFailed,requestFailed 和 responseFailed 这三个失败的回调,他们都会在失败之后重试,并重复执行阶段开始的回调(例如 connectStart),因此针对这些需要单独记录好每一次失败信息,避免被覆盖。


例:某个图片请求 TCP 建连失败的记录


APM 流量监控

尽管我们有专门的网络诊断工具,但是考虑到网络异常的滞后性,故障往往是数十秒之前引入的(例如进电梯弱网),而发生问题之后才触发的网络诊断结果仅能作为参考,并不能作为最终问题归因的证据。


因此我们需要一个更直观的表达用户设备网络状况的数据,那就是通过系统 API 获取的当前 App 流量消耗,在排除了一些本地 socket 通信产生的干扰之后,最近 N 秒之内消耗的流量/N 就可以认为是白屏问题发生时的网速。


我们以 3 秒为间隔分段计算并取最大值来排除波动,这样我们就得到了白屏问题发生前一段时间内的网速状态,过低的就可以判定为弱网/无网。


网速计算示意图:


CDN 异常记录

CDN 厂商侧的异常,例如服务器 IP 跨省,节点挂了等问题相对少见,在网络日志中通常就表现为建连超时但是网络正常,因此需要收集多个 Host 的请求记录进行横向对比来确认是单个 CDN 厂商的问题。


为此我们记录了主站接口的域名,以及所有 CDN 域名在最近 N 分钟内的请求统计,包括了正常请求、慢请求、失败请求的数量和原因,以及失败请求使用的具体 IP。


例:图中可以看到某 CDN 的请求全部都是建连失败,而其他 CDN 以及我们主站域名请求均正常,那么此问题可以归类为该 CDN 的单点问题,联系 CDN 服务商剔除该 IP 所在节点即可解决问题。


网络库请求缓存

常规的网络监控方案为了节省内存,会在请求结束时将其从内存缓存队列中移除,而白屏问题因为其滞后性,只有在屏幕中大面积出现白图才会被判定,此时再去查询加载失败的图片网络请求日志自然早已被移出队列,因此需要单独使用一个 LRU 队列来缓存最近 N 条网络日志,图片库同理。

网络诊断工具

在逐步解决用户问题的过程中,我们研发了网络诊断工具,实现了基础网络信息采集、Ping、TraceRoute 等。


当检测到用户发生白屏时自动触发网络诊断。


单域名诊断流程

我们会依次进行 DNS、Http、Ping(ICPM)与 Ping(TCP)、TraceRoute 诊断。如果是双栈客户端(同时返回了 IPv4 与 IPv6),那么我们会选择首个 IPv4 与首个 IPv6 同时进行 Ping/TraceRoute 以确认不同 IP 类型的联通情况。


多域名诊断流程

同时对 3 个得物核心域名发起诊断。为了辅助判断,当得物核心域名诊断失败时,也会对一些主流三方域名稍微 Ping 一下。

诊断数据的上报

由于网络诊断可能耗时 10 秒以上,而如此长的时间内用户如果退出 App 那我们的诊断数据可能就丢失了。因此,任意一个小阶段的诊断完成时,我们就立即上报此结果,以避免此类情况的发生。

四、CDN 质量监控


CDN 作为整体白屏问题中重要的一环。在白屏排查过程中,经常遇到接口正常,但 CDN 访问异常的 case。而多云 CDN 的质量问题往往依赖于云厂商,如何衡量和监控云厂商的 CDN 情况成为了关键,故我们自定义了端侧的 CDN 质量指标体系以及配合云厂商推进云端策略的优化。


CDN 质量大盘

在白屏监控平台侧,我们经常能够发现用户侧的 CDN 单点问题,往往存在跨省、跨运营商调度的情况,或者是单节点的高负载响应超时。故我们基于此情况记录了端侧的本地 IP&CDN 远端 IP,建设了 CDN 质量大盘,核心指标如下:



依赖 CDN 质量大盘,我们可以横向、纵向对不同云厂商服务进行 CDN 质量评估工作,由于端侧实际发起的总请求数量统计成本高、到 CDN 侧的请求存在丢失等情况,我们采用了端侧采样上报、统计还原的思路,将得物侧的整体 CDN 指标大盘采样放大比例为 1 / 千分之三(当前采样比例)。过程指标的确会收到采样用户质量的影响,但整体 CDN 趋势、各厂商 CDN 的横向对比还是有很大的参考意义的。


平台截图展示:



省份、运营商



超时率、跨省率



节点情况


CDN 云端监控

如果说端侧 CDN 指标是对 CDN 质量抽象评估的长周期指标,那么云端监控则是 CDN 质量的精确告警的短时效指标。


这部分就是一些较为常见的策略与手段了,如 CDN 节点拨测、CPU 负载监控、异常错误码监控等,并协同 SRE 团队将其同飞书通知打通,实现及时告警。

节点拨测

CDN 节点拨测整体指定的图片拨测阈值当前限制在 200ms。



站点拨测



飞书通知

节点监控

关于到节点后的请求监控,主要依赖于云厂商的监控能力,我们也协同云厂商对核心指标的告警阈值等进行了调优,核心主要为:



此处思考一个问题,如果建连请求无法抵达 CDN 节点,那么即使 CDN 厂商开启了 TCP 级日志,也无法监测到此异常,也就无法及时进行调度调整。


故对于非到节点的建联问题,基于 TCP 建连进行监控是很有必要的,我们基于客户端建连的监控数据,以用户网络环境(网络运营商,地域)视角,分析建连过程是否正常,及时发现异常连接线路,并通过推动 CDN 服务商调整区域 DNS 返回来恢复正常。


监控指标如下




建联失败、异常率



建联 p50、p99 耗时分布

五、网络问题优化治理

DNS 策略优化

从网络监控、白屏监控中我们可以清楚的观察到 DNS 错误是网络阶段导致白屏的最大因素。此外,DNS 错误是所有网络错误中最多的,甚至可以占比 80%以上。



连续 DNS 错误导致白屏

LocalDNS 行为

为什么 DNS 阶段如此脆弱,是我们使用不当,还是其本身性能如此?我们首要做的是观察下 Android 平台下 LocalDNS 的行为逻辑以确认性能较差的原因。


按照 TTL 缓存


通过不停的向系统进行查询 DNS,在抓包中,我们很容易观察到 DNS 的查询逻辑。其中一个重要参数是 TTL(Time to Live),这个参数表明当前查询结果的有效时间。比如说 TTL=7,表明在 7 秒钟内当前结果是有效的、可被缓存的,即使 7 秒钟内再次查询,依然是此结果。


Android 平台严格遵守了此协议,按照 TTL 进行缓存。而较短的有效时间,也意味着我们需要进行域名查找时(冷热启动),大概率系统内没有缓存可用。



DNS TTL 与查询频率


通常 TTL 设置为 120 秒以内,这是因为要考虑到节点故障,能够快速切换。而到端侧查询时,TTL 会在 0~120 之间。低于 120 是因为我们查询此结果的 DNS server 自身已经缓存了一定的时间。


双栈客户端同时查询


双栈客户端上,同时发起 A 类(IPv4)、AAAA 类(IPv4)查询。



某运营商 5G 网络下 DNS 查询及响应


当两类查询都成功响应时,Android 会将两类结果组合起来排序后返回给应用层。无论是 A 类地址先返回,还是 AAAA 类地址先返回,Android 都会将 IPv6 地址放在前面(划重点,后面要考)。


-- app.dewu.com 的DNS查询结果[app.dewu.com/2405:e000:1004:1::f3ff:4b49, app.dewu.com/203.107.32.125]
复制代码


可以看到电信的 LocalDNS 服务器并未遵守 TTL 时间(app.dewu.com 的 TTL 为 60),而是进行了额外的缓存。这是部分运营商为了降低客户端的查询频率(降成本)而搞出的小把戏,不在我们的讨论范围内。


DNS 数据包错误与重试


我们将 DNS 服务器设置为一个虚假的 DNS 服务器,来观察 Android 平台在 DNS 未返回时的重试逻辑。



DNS 重试(主、备、隐藏款)


可以看到,0 秒时向主 DNS 服务器发起查询,5 秒向备 DNS 服务器发起查询,8 秒向 114 发起查询。


现在我们想一下,在我们 DNS 查询时,只要 0 秒的第一次查询向主没有正常返回(比如丢包),即使备选正常,能成功查询到结果也是 5 秒钟后的事情了,而这意味着用户在 5 秒内无法正常的请求网络。然后,用户熟练的打开设置、点击用户反馈、选择白屏、输入些表达欣喜的文字、提交!(嘿,来活了)


那么我们能否通过多次查询来触发系统来同时发起多个查询请求呢?不行。当前 Android 内有一个同步栅栏,多个线程同时查询一个 Host,只会允许一个通过。如果成功时,全部被阻拦的查询一同返回结果;如果失败时,再允许一个查询。也就是说,在这 5 秒内,我们只能静静的等着,让用户也等着。


主/备 DNS 服务器我们可以通过在设置中修改,而 114 这个隐藏款只出现在部分国产品牌上,不同品牌内置的隐藏备用 DNS 服务器也并不相同。


iOS 平台的 DNS 行为


众所周知,iOS 平台下的 DNS 异常率远远低于 Android,甚至差了一个数量级。iOS 为何如此优秀?


  • 较长的缓存时长。当 DNS 的 TTL 较低时(比如 3 秒),iOS 会忽略 TTL 值,直接缓存 1 分钟以上。

  • 积极的重试逻辑。只要 1 秒内未收到响应立即进行多次重试,其中主 DNS 6 次,备 DNS 8 次。


DNS 优化

通过对 LocalDNS 的行为分析,我们看到 Android 平台下的 LocalDNS 查询极其不可靠,好在我们并非只能通过 LocalDNS 来解析 IP。任何将域名转换为 IP 的手段都可以使用,比如通过 http 获取、磁盘缓存等。


在 LocalDNS 的基础上,我们增加了 HttpDNS,磁盘缓存来优化 DNS 问题。其中 HttpDNS 在第 60ms 异步启动,磁盘缓存在第 6 秒钟时异步启动。三种查询方式任意一个返回,则 DNS 结束。



HttpDNS


我们实现 Http 进行 DNS 查询,并将其作为备用 DNS 使用。在 LocalDNS 60ms 未成功返回时,异步启动 HttpDNS 进行查询。这将能解决 LocalDNS 下主 DNS 失败 5 秒后才会进行下一次重试的弊端。


HttpDNS 的实现方式,我们暂时选择接入成熟的三方 HttpDNS,快速解决用户在 DNS 方面的困境是主要原因之一。


当然,也并非全部域名通过 HttpDNS 查询都会有效果,比如 localhost、IP 地址等就需要从 HttpDNS 的查询中排除。


同步栅栏:当存在单个 host 的多个查询线程时,仅允许第一个线程查询,其他全部等待查询结果。(类似于 Android 系统对多线程同时查询时的处理)


磁盘缓存


DNS 查询成功时,我们将查询结果缓存到磁盘上。在 LocalDNS、HttpDNS 都未如期返回时,将磁盘上缓存的结果返回给应用层。如此以来,只要历史上这个用户查询成功过,我们始终不会失败在 DNS 阶段。


需要注意的是,在磁盘上缓存 IP 时,需要按照网络类型、运营商进行缓存。原因是不同网络下的最佳 IP 往往不同,比如同一个 CDN 域名中国移动的 CDN 节点与中国电信的 CDN 节点会在各自的机房里。如果我们不分开存储,那么用户网络切换后,我们磁盘缓存出的 IP 或许可用,但可能不是最佳(延迟更高)。


此外,我们的磁盘缓存也完全忽略了 DNS TTL。原因是我们将磁盘缓存作为其他 DNS 查询全部失败后的兜底手段使用,而非主要手段。即使 TTL 过期的 IP 可能会存在一些问题,但也不会有更坏的结果了。


收益情况


通过线上实验观察到,DNS 异常率降低了 60%以上。



不同配置下的 DNS 异常率对比

IPv6 故障修复

前文提到对于双栈客户端,Android 会将 IPv6 地址放在前面返回给应用层。本意上,IPv4 地址即将枯竭而向 IPv6 过渡的方式,而这恰恰是 Android 平台在 TCP 建连阶段时遇到的最大问题。



用户 A-DNS 结果


我们来看一个例子,用户 A 访问 cdn.poizon.com 是 DNS 共响应了 10 个 IPv6、10 个 IPv4,然而此用户 IPv6 无法访问,且给个 IPv6 都是建连 10 秒后超时,最终导致用户在第 100 秒时才在第 11 个 IPv4 地址上成功。



RFC6555 摘要


正如 RFC6555 摘要中描述的那样,当服务器的 IPv4 路径和协议正常,但 IPv6 路径和协议不工作时,相对于 IPv4 单栈客户端,双栈客户端会经历显着的连接延迟。而这个延迟通常是 10 秒以上(IPv6 的个数和建连超时时间的乘积)。


那么如何优化?


最简单的方式是禁用 IPv6,简单到甚至只需要运维动动小手即可。但是由于众所周知的原因,我们国内对于推动 IPv6 十分积极,对于 IPv6 的浓度动不得。


最合理的方式自然是按照 RFC6555 描述的那样实现快乐眼球算法,但是开发成本和风险都较大。而线上时而有类似反馈。因此我们决定分两步走,先上线低风险的 IPv6 探测 &重排序解压此困境,再实现快乐眼球算法。


OkHttp5.x 已支持 RFC6555,但是目前尚未正式发布。且得物历史上对源码的修改较多,整体迁移到 5.x 成本较高。

IPv6 探测 &重排序

此实现的思路是既然 IPv6 可能故障,那么提前探测潜在故障。通过调整即将建连的 IP 列表的顺序来解决客户端建连到 IPv4 的延迟问题。


此实现在 DNS 查询成功之后,建连之前。因为不涉及修改 OkHttp 源码修改所以风险可控。


IPv6 探测


如果 IP 列表中同时包含 IPv6&IPv4,则对第一个 IPv6 地址同步进行 Ping 探测。如果 250ms 内探测成功或曾经探测成功,则认为 IPv6 正常。


重排序


如果 IPv6 畅通,则按照 IPv6 优先进行交叉排序。如果 IPv6 故障,则按照 IPv4 优先进行交叉排序。


# 重排序前[    "imagex-cdn.dewu.com\/2409:8c54:b010:b:8000:0:b00:13",    "imagex-cdn.dewu.com\/2409:8c54:b010:b:8000:0:b00:10",    "imagex-cdn.dewu.com\/183.240.183.19",    "imagex-cdn.dewu.com\/183.240.183.15"]# 重排序后(IPv6探测失败)[    "imagex-cdn.dewu.com\/183.240.183.19",    "imagex-cdn.dewu.com\/2409:8c54:b010:b:8000:0:b00:13",    "imagex-cdn.dewu.com\/183.240.183.15",    "imagex-cdn.dewu.com\/2409:8c54:b010:b:8000:0:b00:10"]# 重排序后(IPv6探测成功)[    "imagex-cdn.dewu.com\/2409:8c54:b010:b:8000:0:b00:13",    "imagex-cdn.dewu.com\/183.240.183.19",    "imagex-cdn.dewu.com\/2409:8c54:b010:b:8000:0:b00:10",    "imagex-cdn.dewu.com\/183.240.183.15"]
复制代码



IPv6 探测 &重排序 流程图

快乐眼球(HappyEyeball)

当前 DNS 返回的 IP 地址数量超过一个时,每 250ms 异步启动一个新的 TCP 建连,任意一个 TCP 建连成功,则断开其他 TCP 并开始同步 TLS 建连。


TLS 建连成功则返回此 Connection,流程结束;TLS 建连失败时,如果是可恢复的 TLS 失败(部分 SSLException),则立即重试;如果是不可恢复失败,则再次启动其他 TCP 的竞速。


核心修改点是原流程的建连部分。原流程中,在网络线程中同步的、依次进行 TCP、TLS 建连。新流程中,将 TCP 与 TLS 建连分开,竞速建连 TCP,TCP 建连完成后建连 TLS。


  • OkHttp3.x 建连流程



OkHttp3.x 建连流程


注:OkHttp3.x 的 nextRoute 之间的循环在 RetryAndFlow 实现


  • OkHttp-HappyEyeball 建连流程



OkHttp-HappyEyeball 建连流程


具体实现方式可以参考 OkHttp5.x 代码


收益情况


自 IPv6 优化上线后,再无相关 IPv6 相关用户反馈。

六、CDN 质量问题治理


从白屏监控大盘中可以看到,CDN 的问题可能只占据 1%~2%,但是不同于通用网络问题,用户凭持着其他 App 可以用,但是得物 App 为什么白屏的疑问,是极容易引起线上反馈的一种 case。故我们也是协同 SRE 团队、云厂商一起做了一些优化手段,力争在现有条件下将端侧的图片 CDN 质量调整到最优。

返回 IP 策略优化

从网络优化来看,DNS 优化以及 IPv6 探测 &重排序为我们解决了双栈 IPv6 问题、单 IP 不可用等问题,但 CDN 问题本身由于不同省份、不同区域等节点数量、网络硬件质量均存在差异,这部分是端侧无法弥补的,故我们想到了优化 CDN 返回 IP 策略的思路来规避以上问题。



返回 IP 优化策略


过程中考虑到得物 IPv6 浓度问题,最早期是返回了 3 个 v4 IP、3 个 v6 IP 的策略,但发现 v6 IP 数量变多后,由于 LocalDNS 天然会把 v6 IP 排放在 v4 IP 前面。此举在没有建连竞速优化的老版本下,会导致 v6 不通的建连时间被逐步拉长 30s。故我们在尽可能保证 v6 浓度的情况下,将返回的 v6 IP 数量降低到了 2 个。


由于 v6 IP 优先请求的情况,我们考虑优先保证 v6 IP 的本省同大区覆盖,故对 v6 IP 的本省的大区返回粒度会比 v4 IP 更细些。


  • 跨省调度问题


针对 v4 IP 本省 2 个大区至少返回 2 个 IP 针对 v6 IP 本省 1 个大区至少返回 1 个 IP


  • 单 ip 返回、v4 不返回问题


针对 v4 IP 至少返回 3 个针对 v6 IP 至少返回 2 个

云厂商优化策略

为了保证图片 CDN 的节点质量,我们积极同 CDN 厂商进行沟通协作,进行了部分优化尝试。

图片专属节点

将原有云厂商的图片、视频、文件等通用节点,迁移至图片专属的 L1 节点,最大程度保证图片请求稳定性和效率。


H2 协议栈优化

过去发现请求耗时增大的一个 case 是 H2 弱网阻塞,在低质量的网络环境下使用 HTTP/2 协议时可能遇到的性能问题或阻塞问题。


云厂商均多次调整了 H2 协议栈的算法优化逻辑,并对比了优化前后的慢请求监控情况。


  • 观察调整省份节点水平,调整后平均耗时下降 15%、慢请求率下降 23%左右。



请求耗时大于 5000ms 的优化情况

节点动态剔除细化

我们针对云厂商的到端链路监控以及白屏用户反馈的潜在非到端问题,通过平台归因 1min 内的 CDN 请求 & 网络诊断的情况,并经过人工确认为 CDN 单通问题后,进行节点剔除切换,优先处理节点抖动带来的影响面问题。


  • CDN 节点的可用性探测判断节点持续抖动并最终剔除的时效在 15min 内;

  • 白屏反馈用户的 sop 应急响应剔除时效从 2~3 小时降低到 30min 内。


CA 证书问题

除了建连阶段的问题外,TLS 等证书认证问题上也有较多的用户反馈,同时白屏平台也有较多类似的错误。




历史为了优化 SSL 耗时过长、证书认证安全等问题,接口、图片域名均开启了 ocsp 的功能,但实际监控发现部分用户存在本地时间不对齐的情况,即超过了 CA 证书 ocsp 校验的有效期(一般 7 天) 或者超过了域名的证书有效期(一般 2 年)。此类用户会导致所有图片请求均被证书拦截而无法完成请求最终引发白屏。


ocsp 问题


  • TLS 耗时开启前后并无实际收益,对于网络耗时的优化可忽略不计;

  • 证书的安全性问题,由于图片域名访问为 CDN 资源非接口核心信息,关闭风险可控;

  • 对比主流站点的 ocsp 开启情况,主流 App 的图片域名大多未开启。



ocsp 的当前时间和下次更新时间


故我们最终关闭了图片域名的 ocsp 功能,整体请求异常数、请求耗时并无劣化且图片 ocsp 的问题反馈数逐步清零。


域名 CA 证书替换失效问题


得物目前有 100+的域名,CA 证书的申请对于 CA 机构来说,一般在证书有效期小于 30 天时会进行新证书的签发流程,且证书有效期以申请期时间为准。但过往的证书有效期更新替换后,往往会面临时间前置的用户无法通过 CA 证书校验,最终引起用户白屏。



我们协同运维侧制定了较为严格的图片域名证书更新时效期,并在多个主域名按此标准落地。



  • 证书 30 天到期自动告警能力,及时跟进证书审批事项;

  • 证书替换选择在有效期提前 3 天,避免无规律的申请即更新的情况。

七、总结


网络问题作为用户白屏的主要原因之一,如果不能及时针对性的优化将对大量用户产生困扰。我们在以解决用户白屏为目标的行动中,逐步完善了客户端网络监控、云端 CDN 监控。并在此过程中完成 DNS、建连、证书、运营商调度等方面的优化,杜绝了某些特定错误的发生,保障了更多用户的网络体验。


近期我们会继续推出一篇介绍如何在客户端监控白屏问题,以及平台如何对白屏问题自动化归因的文章,敬请期待。




*文 / 厉飞雨、GavinX、Jordas


本文属得物技术原创,更多精彩文章请看:得物技术


未经得物技术许可严禁转载,否则依法追究法律责任!

发布于: 刚刚阅读数: 5
用户头像

得物技术

关注

得物APP技术部 2019-11-13 加入

关注微信公众号「得物技术」

评论

发布
暂无评论
得物App白屏优化系列|网络篇_android_得物技术_InfoQ写作社区