移动端网络优化实践:从DNS解析到弱网传输的全链路改造

发布时间:2026/9/16 22:23:17
移动端网络优化实践:从DNS解析到弱网传输的全链路改造
那段时间高德APP的投诉量突然飙升原因集中在几个看似不相关的场景导航过程中路线加载转圈、隧道里路况刷新卡死、地下停车场出口迟迟等不到定位恢复。后台一查问题根本不在地图引擎而是网络层——DNS解析超时、TCP连接反复失败、弱网下的重试策略非但没有救回请求反而把用户通道彻底堵死。于是我们下决心对高德APP的网络链路做一次系统性改造方向就是从DNS解析到弱网传输的全链路优化。这篇文章想把这套优化实践完整复盘出来主要包括如何建立可量化的网络指标体系、DNS环节从LocalDNS迁移到HTTPDNS的落地细节、连接与传输层怎么把成本压到最低、弱网下的超时与重试策略怎么定、弱网测试如何建模与复现以及上线前必须做好的监控验证。负责移动端网络优化的同学、做地图或出行类业务的开发者以及所有被线上网络问题折腾过的人应该都能从中找到可复用的思路。1. 网络优化的起点先建立能度量一切的指标体系1.1 一次线上事故暴露的度量真空刚开始我们并没有直接动代码而是先做了一次全面的线上请求耗时拆解。结果很尴尬监控平台上只有“请求失败率”和“平均耗时”两个指标真出了线上问题连“慢在哪一段”都说不清。记得某个一线城市的晚高峰路况接口超时率突然从1%飙到15%。服务端同事说网关一切正常客户端同学说代码没发新版两边拿着各自的监控面板对质谁也没法证明问题出在哪。最后从几十个用户日志里手工翻才发现大量请求卡在“TCP连接建立”阶段压根没到服务端。原因其实是那个区域基站拥塞新建连接全部超时。这次事故之后我们想明白了一件事网络优化第一优先级不是改协议而是先把监控指标建起来否则后续所有优化都是在黑暗中开枪。1.2 把一次网络请求拆成六个阶段移动端一次HTTPS请求从用户点击到页面呈现中间要经历六个关键阶段阶段定义常见耗时区间主要优化手段DNS解析域名解析为IP地址20-200msHTTPDNS、预解析TCP建连三次握手1个RTT连接复用、连接池TLS握手HTTPS安全握手1-2个RTTTLS1.3、会话恢复请求上行客户端发送请求体与上行带宽相关请求瘦身、压缩TTFB等待服务端返回首字节50-500ms服务端优化、就近接入内容下载接收响应体与包大小和带宽相关压缩、缓存、增量更新为什么要拆这么细因为网络优化最大的风险是“瞎猜”。平均耗时下降不代表用户感知变好必须同时盯P50和P95。P50代表大多数用户的体验P95代表最差情况的天花板。弱网场景下少量极端耗时会把平均值拉得很高但真正影响口碑的是P95那部分用户。把六个阶段拆开之后每个阶段都有独立的优化手段排查问题时也能直接定位到具体环节。1.3 北极星指标与优化基线拆解完阶段后我们定了四个北极星指标首包成功率、首屏耗时、网络错误率、弱网请求占比。这四个指标不能只看整体均值必须按网络类型Wi-Fi/4G/5G、运营商、区域维度分别统计。指标的基线不是拍脑袋定的而是采集了至少两周的全量上报数据。这里有个经验全量上报很耗流量和电量我们最终采取“慢请求必报 正常请求随机采样10%”的策略。所谓慢请求是指耗时超过1秒或失败的网络请求这类数据本身就有排查价值全部保留正常请求按10%采样足以估算大盘趋势又不会给用户带来额外负担。基线数据出来之后团队内部达成了共识DNS解析阶段P95要降到50ms以内首包成功率要从99.0%提升到99.5%以上。有了清晰目标后面的优化才有的放矢。2. DNS环节从LocalDNS到HTTPDNS的改造与取舍2.1 传统DNS在移动端为什么又慢又不稳本地DNS运营商LocalDNS的解析过程是从客户端发一个UDP请求到运营商递归服务器递归服务器如果没有缓存还要去根服务器、顶级域服务器、权威服务器逐级迭代查询。整个过程在移动网络下很容易达到上百毫秒遇到弱网直接超时也不罕见。传统DNS在移动端有三个老问题慢。移动网络链路本来就长加上运营商递归服务器的策略差异解析耗时波动很大。高德APP域名多冷启动阶段涉及路线、路况、检索等多个域名每个都走一遍本地DNS几百毫秒就没了。容易被干扰。公共Wi-Fi环境或者运营商链路上DNS响应可能被篡改返回一个错误IP轻则请求失败重则被导到无关页面。调度不精准。运营商LocalDNS通常没有按用户真实地理位置返回就近节点的能力。我们实测过有南方城市的用户被解析到华北的节点地图瓦片加载相当于跨省传输速度自然感人。2.2 HTTPDNS的原理与落地细节HTTPDNS的思路很直接客户端不再走本地UDP 53端口而是通过HTTP接口向自建或第三方DNS服务器请求域名对应的IP列表。返回结果一般是JSON里面带IP列表、TTL、运营商线路等信息。核心接入逻辑可以简化成下面的伪代码class HttpDnsManager( private val dnsServer: String ) { private val cache ConcurrentHashMapString, DnsRecord() suspend fun getIp(host: String): String? { // 先读本地缓存命中且未过期直接返回 cache[host]?.let { record - if (!record.isExpired()) return record.ips.first() } return refresh(host) } private suspend fun refresh(host: String): String? { return try { // 向HTTPDNS服务端发起解析请求 val resp httpClient.get($dnsServer/dns?host$host) val record parse(resp.body()) // 缓存时间不能完全信任服务端返回的TTL // 客户端要设置一个最大上限 cache[host] record record.ips.first() } catch (e: Exception) { // 解析失败返回null由调用方降级到系统DNS null } } }接入HTTPDNS有几个坑必须提前处理。第一个坑是缓存策略。服务端返回的TTL哪怕只有30秒在高频请求下也会产生大量重复解析。我们的做法是基础TTL缓存同时叠加一个客户端最大缓存上限比如10分钟冷启动时主动刷新一次核心域名请求失败时再主动老化一次。第二个坑是降级容灾。HTTPDNS服务也可能挂客户端到HTTPDNS服务器的网络也可能断。必须保证HTTPDNS不可用时可以快速回退到LocalDNS不能让DNS解析成为单点。我们当时的降级策略是HTTPDNS解析超时设为1.5秒超时或者HTTP状态码非200时立刻切回系统默认解析。第三个坑是IP直连后的Host头处理。拿到IP后请求URL里的域名要替换成IP但HTTP请求头里的Host字段必须保留原域名否则服务端无法识别虚拟主机HTTPS证书校验也会失败。2.3 域名分级与预解析策略高德APP的域名有好几十个不可能全部走HTTPDNS那样既耗流量又可能拖慢启动。我们把域名按重要程度分成了三类核心请求域名路线规划、路况、搜索、导航必须走HTTPDNS要求解析结果稳定可靠。中等重要域名图片、日志上报、配置下发走LocalDNS加短缓存即可偶尔解析慢不影响主流程。低优先级域名活动页埋点、运营数据统计之类的请求晚个几百毫秒无所谓用系统默认解析就行。预解析的触发时机也有讲究。我们最终只在三个位置做预解析APP冷启动后马上预解析首屏需要的核心域名用户发起导航之前预解析路线沿途可能要访问的服务域名网络状态从不可用恢复为可用时重新预解析一遍核心域名。预解析如果放错位置比如启动时把所有域名全解析一遍在弱网下只会让网络链路雪上加霜。3. 连接与传输层把每一次请求的成本压到最低3.1 一次HTTPS请求的握手成本不容小觑DNS解析完成后紧接着就是TCP建连和TLS握手。TCP三次握手大约消耗1个RTTTLS1.2握手要2个RTT加一起就是3个RTT。在信号良好的Wi-Fi下RTT大概是10到20毫秒影响不大但在4G弱网环境下RTT能到100毫秒以上光建连接就要300多毫秒。服务端哪怕只处理50毫秒用户感知的首包时间也已经接近400毫秒。如果每个请求都新建连接这成本完全不可接受。所以连接复用在整条优化链路里优先级非常高。3.2 连接池、Keep-Alive与HTTP/2多路复用移动端的连接池和服务端不太一样。服务端连接池只需要按目标地址维护移动端还要考虑网络类型切换。Wi-Fi和4G切换后旧连接基本失效如果直接从连接池里拿出来用大概率超时。我们的连接池按“目标域名 当前网络类型 是否需要代理”三个维度做Key网络切换时主动清理旧条目避免拿到死连接。在此基础上我们把基础网络库全面切到HTTP/2。HTTP/2的多路复用允许一个TCP连接上同时跑多个请求每个请求独立Stream互不阻塞头部压缩HPACK对地图这种请求头里带大量认证信息的接口收益尤其明显。切换之后最直观的变化是并发请求不再排队等新建连接整体连接数降了一个数量级。3.3 长连接与QUIC的评估结论地图导航场景还有一个特殊需求实时路况推送需要长时间维持一条连接。TCP长连接在移动网络下要面对心跳保活、NAT超时、网络切换断连一堆问题。我们当时认真评估过QUICHTTP/3。QUIC最吸引我们的能力是连接迁移。手机从Wi-Fi切到4GTCP连接基本要重建但QUIC使用Connection ID识别连接IP变了连接ID不变上层会话可以继续。这对导航场景太重要了——用户一边走路一边导航跨基站或切换热点时路况推送通道不应该断掉。不过QUIC落地也有现实阻碍UDP流量在某些网络环境会被限速甚至丢弃部分中间设备对UDP长连接不友好客户端和服务端的CPU开销比TCP更高。我们当时的结论是不全面切换先拿实时路况推送通道做试点。如果你们团队人力有限建议先把TCP/TLS和HTTP/2优化到位再考虑QUIC。4. 弱网环境下的请求策略不是所有重试都是对的4.1 弱网识别与分级信号格数不等于网络质量很多人以为手机信号格数代表网络质量这是个大误区。演唱会现场4G信号满格但基站拥塞实际可用带宽可能不到0.5Mbps高铁上信号格数随时跳动切换基站时丢包率飙升。信号格数只能说明终端和基站之间的无线链路状态不代表端到端网络质量。我们采用的弱网判定方案是“请求实测数据打分”客户端基于最近N次请求的DNS耗时、建连耗时、TTFB、丢包率和下行速度用滑动窗口计算一个网络质量分数每30秒左右更新一次。然后按分数把网络分为四个等级网络等级RTT丢包率可用带宽应对策略良好 200ms 2% 1Mbps正常请求不做额外限制中等200-500ms2%-5%300Kbps-1Mbps降低图片质量开启压缩弱网500-1000ms5%-10%100-300Kbps缩短超时仅发核心请求极弱 1000ms 10% 100Kbps取消非必要请求等待恢复这套分级不是死的会根据不同业务场景动态调整。比如导航中的路线刷新在极弱网络下可以选择暂停但用户手动发起的路线请求仍然要尝试。4.2 超时、重试与退避别把弱网用户拖死弱网环境下超时时间设置很考验功力。设得过大用户会看一个请求转圈几十秒设得过小好网络下也可能误判失败。我们的做法是不让超时一刀切而是按网络等级动态调整请求类型良好网络连接超时/读超时弱网连接超时/读超时极弱网络连接超时/读超时核心请求路线/路况3s / 5s2s / 5s1.5s / 4s普通请求图片/检索2s / 4s1.5s / 3s1s / 2s日志上报2s / 3s1s / 2s不请求注意核心请求在弱网下连接超时反而短是因为弱网下新建连接成功率很低与其让用户干等不如快速失败走缓存或提示用户。读超时留得稍长一些因为请求一旦发出去服务端可能还在处理。重试策略更得谨慎。移动端最容易犯的错是“失败就立即重试连续重试三次”这在线上故障时会造成重试风暴把本来就紧张的服务端压垮。我们给重试定了三条铁律只有可重试的失败才重试。网络超时、5xx可以重试4xx、DNS解析失败这类不需要重试。重试必须退避加抖动。第一次等1秒第二次等2秒第三次等4秒每次加0-10%的随机抖动避免客户端步调一致。幂等性是重试的前提。定位上报、日志上报这类幂等请求可以重试下单、确认路线这类强一致请求绝不自动重试。4.3 请求瘦身、压缩与预取让弱网传输的数据更少弱网环境下传更少的数据才是硬道理。我们在数据体积上做了四件事所有HTTP响应开启Brotli压缩压缩率比gzip再提升10%-20%。地图瓦片从JPEG逐步迁移到WebP同清晰度下体积减少30%左右。接口数据从JSON逐步迁移到Protobuf特别是高频的路况、位置上报接口体积直接减半。瓦片按级别做增量更新用户只下载变化的部分而不是整包刷新。预取是另一个关键手段。用户发起导航时先把路线方案和沿途交通事件请求发出去用户搜索某个POI时提前把POI周边的路况数据拉下来。因为导航启动后的几秒内网络通常还可用等用户开进隧道再请求就晚了。预取不能太贪心预取太多无效数据会白白消耗流量和电量我们结合用户行为做预测命中率能做到50%以上不命中的数据直接丢弃。5. 弱网模拟与多端联调如何复现线上网络问题5.1 常用弱网模拟工具的选型优化策略做了一大堆总要验证到底靠不靠谱。日常开发中我们试过好几类弱网模拟工具各有优劣工具平台能模拟的参数缺点适用阶段CharlesmacOS/Windows带宽、延迟、丢包、MTU手机需要配代理性能一般日常功能联调FiddlerWindows限速为主丢包能力弱丢包模拟不直观Windows环境联调Network Link ConditionermacOS/iOS模拟器带宽、延迟、丢包、DNS延迟真机需要配合Mac调试首屏加载、接口超时自测Android模拟器Android延迟、丢包、带宽模拟器和真机差异大快速功能验证自研弱网代理Linux带宽、延迟、丢包、抖动、乱序需要一定的开发维护成本CI回归、压测Charles和Fiddler适合开发阶段快速验证但模拟力度有限。Network Link Conditioner可以模拟3G/4G网络和自定义参数但对iOS之外的环境支持有限。我们最终在CI环境里自建了一个弱网代理本质是在Linux服务器上用tc和netem在网络层注入可控的延迟和丢包。# 在代理网卡上模拟200ms延迟 5%丢包 sudo tc qdisc add dev eth0 root netem delay 200ms loss 5% # 清除规则 sudo tc qdisc del dev eth0 root netem再次强调这条命令只在测试环境使用线上服务器千万不要乱执行。自建代理的好处是可以把弱网参数做成配置化测试同学写用例时直接指定带宽、延迟、丢包率CI流水线里自动跑省去大量手工调试时间。5.2 按真实场景建模的弱网用例弱网测试不能只调一组“慢速网络”参数要贴近真实场景。我们沉淀了一套场景化用例库场景带宽延迟丢包验证重点地下停车场/隧道2Mbps100ms5%路线加载失败后恢复、缓存是否兜底电梯1Mbps200ms10%快速进出电梯时连接恢复、请求不悬挂演唱会/大型赛事0.5Mbps50ms2%低带宽拥塞下图片加载策略、请求优先级高铁10Mbps60ms10% 抖动基站切换时连接不断、重试不风暴每个场景不仅验证“请求能不能成功”还关注“失败后用户体验是否可接受”。比如地下停车场场景我们要求路线加载失败后5秒内出现重试按钮或缓存路线绝不允许一直转圈。这些用例后来接入了发布流水线每次发版前自动跑一遍核心接口有效防止网络策略被业务迭代意外改坏。强烈建议有条件的团队也这么做成本不高收益非常稳定。5.3 线上弱网问题怎么定位弱网问题在测试环境复现后第一步不是看业务代码而是先确认请求到底有没有发出去、卡在哪个网络环节。用抓包工具看最直接。抓包时一般看几个信号有SYN发出但没有SYN-ACK服务器不可达或中间网络丢弃。SYN-ACK回来但客户端没回ACK多半是客户端本地网络问题比如弱网下上行能力不足。TLS ClientHello发出后没有ServerHelloTLS握手被中间设备干扰或服务端TLS配置异常。请求发出后迟迟没有第一个响应包服务端业务处理慢或网关堵塞需要结合服务端日志判断。为此客户端每个网络请求都要生成一个traceId发请求时通过HTTP头透传给服务端客户端日志和服务端日志都用同一个traceId。这样排查问题时无论先看客户端还是服务端都能把整条链路串起来。我们踩过很多次“各看各的日志、谁也没法证明自己没问题”的坑traceId是解药。6. 优化上线的最后一公里监控验证与问题排查6.1 建立网络监控大盘优化不是上线就结束监控必须变成日常机制。我们的网络监控大盘包含四块请求成功率按域名、网络类型、运营商维度实时统计。各阶段耗时DNS解析、TCP建连、TLS握手、TTFB、下载耗时的P50/P90/P95。错误码分布超时、连接拒绝、DNS失败、HTTP状态码等分类聚合。弱网请求占比按城市、时段观察弱网流量变化。每天早上团队过一遍大盘任何指标突变都能第一时间发现。有一次我们发现某城市P95耗时突然翻倍点开一看是该区域一个HTTPDNS节点的丢包率异常立刻把流量切到了备用节点。6.2 灰度发布与效果评估网络优化最怕的就是拍脑袋全量上线。任何策略改动都要先小流量灰度维度可以是用户ID末尾一位、APP版本、城市、网络类型。我们常用的灰度节奏是2%、5%、10%、20%、50%、100%逐步放量每一档至少观察24小时确认无异常再继续放。效果评估不能只看网络指标还得看业务指标。之前有一次连接复用灰度请求耗时明显下降但用户反馈反而变差查了半天发现是新的IP调度策略把用户导到了距离更近但负载很高的节点。后来我们规定灰度评估必须同时观察以下指标网络成功率、各阶段耗时P95。业务首屏成功率、导航成功率。崩溃率、卡顿率。主路径PV/UV转化率。6.3 三个线上排查实战案例最后分享三个实战案例都是真实发生过的问题希望你能少走弯路。第一个案例是关于HTTPDNS降级救命的。某次某运营商递归DNS出现大面积异常很多APP用户访问超时。因为我们已经接入了HTTPDNS并且降级策略做得比较完善一部分流量走HTTPDNS正常返回一部分降级到LocalDNS仍然受影响但整体恢复速度比同行快了大约两小时。这次之后我们把HTTPDNS的覆盖率从70%提升到了90%以上并且要求所有核心域名必须走HTTPDNS。第二个案例是隧道中请求悬挂。用户投诉进隧道再出来APP长时间无响应。排查发现TCP连接在隧道内超时后客户端在恢复网络时仍然拿着旧连接发送数据一直在等旧连接超时等超时时间到了又开始指数退避几十秒内没有发出任何新请求。修复方案是监听网络切换事件主动关闭旧连接池强制新建连接。这次之后我们在连接池里增加了“网络代际”标记每次网络恢复都会触发全局连接失效。第三个案例是活动接口的重试风暴。一次运营活动开始时瞬时并发很大服务端少量请求返回503客户端自动重试。因为重试抖动不够随机大量客户端在同一秒发起第二次请求服务端压力进一步放大。后来我们在SDK层做了全局重试去抖同一接口、同一用户、1分钟内最多自动重试2次且重试间隔完全随机化。这个机制之后再也没因为重试引发过故障。如果让我给准备做网络优化的团队一条最实用的建议先别急着换协议、换框架把每一段网络耗时精准量化先采两周基线数据再动手改第一行代码。网络优化没有银弹HTTPDNS、连接复用、QUIC这些都只是手段真正重要的是建立起一套“可度量、可定位、可验证”的闭环。有了这个闭环优化方向自然会清晰地浮出水面。