网络排障专项
网络排障的核心不是背 HTTP 状态码,而是把一次请求拆成 DNS → TCP → TLS → HTTP → 业务解析 → 本地缓存/重试。Android 面试尤其看你能否用 OkHttp、Charles、日志和弱网策略定位真实线上问题。
一、移动端网络排障总流程
先按链路分层,不要一上来就改超时或重试。每一层都要有证据:耗时、错误码、异常类型、抓包结果、服务端日志。
用户反馈慢/失败
↓
确认网络类型/Wi-Fi/蜂窝/代理/VPN
↓
DNS 解析 → TCP 连接 → TLS 握手 → HTTP 请求/响应
↓
OkHttp EventListener/Interceptor 日志
↓
Charles/服务端 trace 对照
↓
重试、降级、缓存或服务端修复
| 现象 | 优先看什么 | 常见原因 |
|---|---|---|
| 首次请求慢 | DNS/TCP/TLS 阶段耗时 | DNS 慢、冷连接、证书链慢 |
| 只有 HTTPS 失败 | TLS 与证书 | 证书过期、SNI、Pinning、系统时间错误 |
| 4xx | 请求与鉴权 | token 过期、参数错、权限不足、限流 |
| 5xx | 服务端/网关 | 上游超时、发布故障、容量不足 |
| 弱网下大量失败 | 超时/重试/连接池 | 超时太短、非幂等重试、连接复用异常 |
二、DNS 慢与解析失败
DNS 问题常表现为“首包慢、部分地区失败、切 Wi-Fi/蜂窝表现不同”。Android 端要区分 DNS 解析耗时和后续连接耗时。
- 原因:运营商 DNS 慢/污染、IPv6/IPv4 选择问题、DNS 缓存过期、内网域名不可达、DoH/HTTPDNS 配置异常。
- 证据:OkHttp
EventListener.dnsStart/dnsEnd耗时、解析到的 IP、网络类型、地区、失败异常。 - 策略:合理 DNS 缓存、HTTPDNS/DoH、Happy Eyeballs、失败 IP 黑名单、按域名维度监控。
- Android 注意:不要在主线程解析域名;多域名、多 IP 兜底要避免无限重试导致电量和流量浪费。
面试话术:先证明是不是 DNS 慢,再谈替代方案;HTTPDNS 能绕过运营商 DNS,但要处理 HTTPS 证书域名校验、调度准确性和缓存过期。
三、TLS 失败、证书错误与 Pinning 调试
TLS 失败要按“证书链、域名、时间、协议套件、Pinning、代理抓包”逐项排除。
| 错误类型 | 可能原因 | 排查方向 |
|---|---|---|
| 证书过期/未生效 | 服务端证书时间错误 | 检查证书有效期与设备时间 |
| Hostname verification failed | 证书 SAN 不含域名 | 检查域名、SNI、CDN 证书 |
| Trust anchor not found | 自签/链不完整 | 补齐中间证书、network security config |
| Pinning failure | 公钥/证书指纹不匹配 | 检查发布环境 pin 列表与轮换策略 |
| Handshake failed | TLS 版本/套件不兼容 | 老设备、服务端协议配置、ALPN |
Charles 调试与 Pinning 策略:
- 开发/测试包可使用 debug-only
network_security_config信任用户 CA。 - Pinning 必须区分 debug/release:debug 可关闭或使用测试 pin,release 严格校验。
- 不要为了抓包在线上包硬编码关闭 Pinning;应该用构建变体、白名单测试域名或内部证书。
- Pinning 要支持证书轮换:至少保留当前和备用公钥 pin。
四、HTTP 4xx/5xx 与业务错误定位
HTTP 状态码要先分清“协议层状态”和“业务层 code”。移动端常见误区是看到 500 就重试,看到 401 就清登录态,但真实原因可能更细。
- 400:参数格式、签名、时间戳、序列化字段缺失。
- 401:token 过期、刷新 token 失败、设备被踢、匿名接口误带错误凭证。
- 403:无权限、风控拦截、地区/灰度策略不允许。
- 404/405:路径、环境、方法不一致,常见于测试环境配置错。
- 429:限流,客户端要退避而不是立刻重试。
- 500/502/503/504:服务端、网关、上游依赖或超时;客户端要带 traceId 找服务端对日志。
Android 实践:
- Interceptor 统一注入 traceId、版本、网络类型,便于端云对齐。
- 401 刷新 token 要做单飞(single flight),避免并发请求同时刷新。
- 4xx 多数不应盲重试;5xx 可对幂等请求有限退避重试。
五、弱网络重试、连接池与超时设置
弱网策略要平衡成功率、电量、流量和业务副作用。不是“失败就重试三次”这么简单。
val client = OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(15, TimeUnit.SECONDS)
.writeTimeout(15, TimeUnit.SECONDS)
.callTimeout(30, TimeUnit.SECONDS)
.retryOnConnectionFailure(true)
.connectionPool(ConnectionPool(5, 5, TimeUnit.MINUTES))
.build()
- connectTimeout:TCP 建连超时,过短会误伤弱网,过长会拖慢失败反馈。
- readTimeout/writeTimeout:读写单次阻塞超时,适合控制传输阶段。
- callTimeout:一次 call 总预算,避免 DNS/TLS/重试叠加无限拉长。
- 连接池:复用 TCP/TLS 连接降低握手成本;但域名/IP/证书变化、长时间 idle、网络切换会导致旧连接失效。
- 重试原则:GET/查询类可退避重试;下单、支付、登录态变更必须依赖幂等 key 或服务端去重。
- 退避策略:指数退避 + jitter,避免所有客户端同时重试放大故障。
六、OkHttp Interceptors、EventListener 与 Charles
OkHttp 排障工具分两类:Interceptor 看请求/响应内容,EventListener 看阶段耗时。两者结合才能回答“慢在哪里”。
- Application Interceptor:业务层,适合加公共 header、日志、签名、token、业务错误处理。
- Network Interceptor:网络层,能看到重定向、网络响应、缓存细节。
- EventListener:记录 DNS、connect、secureConnect、requestHeaders、responseHeaders 等阶段耗时。
- Charles:验证请求是否发出、header/body 是否正确、TLS 证书链、代理环境下服务端响应。
排障 checklist:
- 打印 URL、method、traceId、状态码、异常类型、各阶段耗时。
- Charles 对照请求头、body、证书和响应。
- 断网/弱网/代理/VPN/IPv6 场景复现。
- 和服务端用 traceId 对齐网关日志。
- 修复后保留监控指标,避免同类问题复发。
高频面试题
Q1:用户反馈接口慢,你怎么定位? 先拆阶段:DNS、TCP、TLS、请求上传、服务端处理、响应下载。用 OkHttp EventListener 记录阶段耗时,Interceptor 打 traceId,Charles/服务端日志对照,再判断是客户端网络、证书、连接池还是服务端问题。
Q2:TLS 证书错误常见原因有哪些? 证书过期、设备时间错误、证书链不完整、域名与 SAN 不匹配、老设备 TLS 套件不兼容、Pinning 指纹不匹配、Charles 代理证书未被 debug 包信任。
Q3:弱网重试怎么设计? 只对幂等或有幂等 key 的请求重试,设置总 callTimeout,使用指数退避和 jitter,限制次数,网络恢复后再补偿。非幂等业务必须服务端去重,不能客户端盲重发。
Q4:OkHttp Interceptor 和 EventListener 区别? Interceptor 适合改请求、加 header、签名、日志和处理响应;EventListener 更适合记录 DNS/TCP/TLS/请求/响应各阶段耗时,用于定位慢在哪里。
易错点 / 追问
- 易错:把 5xx 全部归因客户端;应带 traceId 找服务端/网关日志。
- 追问:HTTPDNS 访问 IP 时 HTTPS 证书怎么校验?仍要用原始域名做 SNI/Hostname verification,不能简单把 URL host 改成 IP 后跳过校验。
- 易错:为了 Charles 抓包关闭 release Pinning;正确做法是 debug-only 策略或测试域名。
- 追问:为什么 429 不该立即重试?它表示限流,立即重试会放大压力,应按服务端提示或指数退避。