网络请求API监控与网络切换埋点

1.背景与目标

本文介绍 Android 工程中网络请求性能监控和网络切换监控的实现方式。

目标是:

  • 对一次 HTTP/HTTPS 请求从发起到结束的完整链路进行一次汇总上报;
  • 分阶段统计 DNS、网络连接、TLS、请求和响应的耗时;
  • 在默认网络可用或网络类型发生变化时上报一次网络切换事件;
  • 不让日志采集或埋点上报影响原始网络请求;
  • 避免记录请求体、响应体、Header 值和 URL query 参数等敏感或高基数内容。

2. 整体架构

text 复制代码
appInit
  └─ android_network_log_open
       ├─ GlobalApp.contextVar.networkLogOpen
       └─ NetworkConnectivityMonitor.update(enabled)

接口请求
  └─ ApiManager / DynamicUrlManager
       └─ OkHttp EventListener.Factory
            └─ NetworkEventListener
                 ├─ LogUtils 输出本地汇总日志
                 └─ NetworkLogReporter
                      └─ StatService
                           └─ SensorsStat.onEvent("networkLog", data)

系统默认网络变化
  └─ ConnectivityManager.NetworkCallback
       └─ NetworkConnectivityMonitor
            └─ NetworkLogReporter
                 └─ StatService
                      └─ SensorsStat.onEvent("networkChange", data)

3. 开关和初始化时机

初始化流程:

  1. Application.setUpService() 将 StatService.instance 注入到 GlobalApp.contextVar.networkLogReporter;
  2. 首次启动请求 discover/appInit;
  3. DynamicWritingManger 缓存 switchConfigListList;
  4. 读取 android_network_log_open,默认值为 false;
  5. 将结果写入 GlobalApp.contextVar.networkLogOpen;
  6. 调用 NetworkConnectivityMonitor.update(networkLogOpen)。

开关行为

  • false:不采集接口性能回调,不注册网络切换回调;
  • true:注册系统默认网络回调,并对后续 OkHttp 请求开启采集;
  • NetworkEventListener 在每次 callStart() 时读取一次开关;已创建的 OkHttpClient 不需要重建;
  • 关闭开关时会注销 ConnectivityManager.NetworkCallback 并清空监控状态;
  • appInit 请求发起时开关通常尚未下发,因此该请求通常不会被这套接口性能日志记录;
  • MainViewModel 的缓存回退结果也会进入成功处理流程,因此缓存中的开关配置仍可能生效。

4. 接口监控埋点

4.1 接入位置

OkHttpClient中接入

java 复制代码
.eventListenerFactory(NetworkEventListener.factory())

4.2 一次请求只上报一次

NetworkEventListener是按 OkHttpCall` 创建的。每个请求实例维护自己的时间戳和采集字段。

请求开始时:

text 复制代码
callStart()
  ├─ 读取 networkLogOpen
  ├─ 记录 callStartNanos
  └─ 保存 method 和 URL

请求结束时:

  • callEnd():以 outcome=success 汇总;
  • callFailed():以 outcome=failed 汇总;
  • logged 标记保证同一个请求不会重复汇总上报。

4.3 请求阶段和回调含义

典型 HTTPS 请求顺序如下:

java 复制代码
callStart
  ↓
dnsStart / dnsEnd
  ↓
connectStart / connectEnd
  ↓
secureConnectStart / secureConnectEnd
  ↓
connectionAcquired
  ↓
requestHeadersStart / requestHeadersEnd
  ↓
requestBodyStart / requestBodyEnd
  ↓
responseHeadersStart / responseHeadersEnd
  ↓
responseBodyStart / responseBodyEnd
  ↓
callEnd 或 callFailed
阶段 OkHttp 回调 统计内容
请求开始 callStart 总耗时起点、请求方法、URL、开关状态
DNS 开始 dnsStart DNS 开始时间、解析域名
DNS 结束 dnsEnd DNS 耗时、DNS 成功状态;当前实现不保存 DNS 返回的全部 IP
TCP 连接开始 connectStart TCP 建连开始时间、目标 IP
TCP 连接结束 connectEnd 建立连接耗时、协商后的 HTTP 协议
TLS 开始 secureConnectStart HTTPS TLS 握手开始时间
TLS 结束 secureConnectEnd TLS 握手耗时
获取连接 connectionAcquired 是否复用连接池连接、补充远端 IP
请求头 requestHeadersStart / requestHeadersEnd 发送请求头耗时
请求体 requestBodyStart / requestBodyEnd 发送请求体耗时、请求体字节数
响应头 responseHeadersStart / responseHeadersEnd 接收响应头耗时、HTTP 状态码
响应体 responseBodyStart / responseBodyEnd 接收响应体耗时、响应体字节数
请求结束 callEnd 总耗时、传输层成功
请求失败 callFailed 总耗时、最终 IO 失败原因

4.4 接口埋点字段

事件名:networkLog

字段 含义
event_type 固定为 request
outcome success 或 failed,表示传输层结果
method HTTP 方法,如 GET、POST
url 仅保留 scheme、host、port 和 path,不包含 query
code HTTP 状态码;未收到响应时为 -1
protocol 实际协议,如 http/1.1、h2
reused 是否复用已有连接
dns_domain 执行 DNS 解析的域名;连接复用时为空
dns_status success、failed、not_started 或 unknown
dns_error DNS 失败异常;正常或未执行时为空
connected_ip Socket 实际连接 IP,不是 DNS 返回的全部 IP;无法获取时为空
network_type 请求结束时默认网络类型,如 wifi、cellular、vpn
dns_ms DNS 解析耗时,单位毫秒
connect_ms 建立网络连接耗时,不包含 DNS;TLS 单独统计
tls_ms HTTPS TLS 握手耗时;非 HTTPS 或未发生时为 -1
request_headers_ms 发送请求头耗时
request_body_ms 发送请求体耗时
response_headers_ms 接收响应头耗时
response_body_ms 接收响应体耗时
total_ms 从 callStart 到结束或失败的总耗时
request_bytes 已发送请求体字节数,不含请求头
response_bytes 已接收响应体字节数,不含响应头
failure 最终失败异常;成功时为空

没有发生或无法采集的阶段统一使用 -1,便于区分"耗时为 0"和"该阶段未执行"。

4.5 示例:书城接口请求

以下示例表示一次使用 Wi-Fi 的 HTTPS 请求:

java 复制代码
{
  "event_type": "request",
  "outcome": "success",
  "method": "POST",
  "url": "https://api.example.com/book/list",
  "code": 200,
  "protocol": "h2",
  "reused": false,
  "dns_domain": "api.example.com",
  "dns_status": "success",
  "dns_error": "",
  "connected_ip": "203.0.113.10",
  "network_type": "wifi",
  "dns_ms": 12,
  "connect_ms": 34,
  "tls_ms": 48,
  "request_headers_ms": 1,
  "request_body_ms": 5,
  "response_headers_ms": 86,
  "response_body_ms": 23,
  "total_ms": 214,
  "request_bytes": 128,
  "response_bytes": 4096,
  "failure": ""
}

说明:connect_ms 按 OkHttp 的 TCP 建连阶段统计,不包含 DNS;HTTPS 的 TLS 握手单独进入 tls_ms。connected_ip 表示最终 Socket 实际连接的 IP,当前实现没有上报 DNS 返回的全部候选 IP。

5. 网络切换埋点

5.1 实现方式

NetworkConnectivityMonitor 使用 Android 系统的:

java 复制代码
ConnectivityManager.registerDefaultNetworkCallback()

监听当前系统默认网络,而不是监听某个具体接口。

5.2 回调处理规则

系统回调 当前处理
onAvailable 获取网络能力,识别网络类型并判断是否需要上报
onCapabilitiesChanged 更新网络能力;默认网络对象或网络类型变化时上报
onLost 如果丢失的是当前默认网络,则上报网络不可用;如果已有新默认网络,则按网络可用切换处理

当前网络类型包括:

java 复制代码
vpn、wifi、cellular、ethernet、bluetooth、wifi_aware、lowpan、other、none

5.3 网络切换字段

事件名:networkChange

字段 含义
event_type 固定为 network_change
network_status available 或 lost
network_type 当前目标网络类型
from_network_type 切换前网络类型
to_network_type 切换后网络类型
from_network_validated 切换前是否通过系统网络验证
network_validated 切换后是否通过系统网络验证
event_time_ms Unix 时间毫秒值
event_elapsed_realtime_ms Android 单调时钟毫秒值

5.4 示例:Wi-Fi 切换到移动网络

java 复制代码
{
  "event_type": "network_change",
  "network_status": "available",
  "network_type": "cellular",
  "from_network_type": "wifi",
  "to_network_type": "cellular",
  "from_network_validated": true,
  "network_validated": true,
  "event_time_ms": 1778822400000,
  "event_elapsed_realtime_ms": 23891234
}

6. 关键边界行为

6.1 连接复用

如果 OkHttp 复用连接池中的连接,通常不会重新执行 DNS、TCP 建连和 TLS 握手:

  • reused=true;
  • dns_status=not_started;
  • dns_ms、connect_ms、tls_ms 可能为 -1;
  • connected_ip 会尝试从已有连接补充。

6.2 DNS 解析 IP 的口径

dnsEnd() 回调参数中包含 DNS 返回的 inetAddressList,但当前实现没有保存该列表。埋点中的 connected_ip 来自 connectStart() 的实际连接地址,连接复用时则从已有 Socket 地址补充。因此当前埋点可以回答"实际连到了哪个 IP",不能回答"DNS 一共解析出了哪些 IP"。

6.3 HTTP 错误码

HTTP 404、500 等属于"拿到了 HTTP 响应",因此传输层通常进入 callEnd(),表现为:

text 复制代码
outcome=success
code=404 或 500

outcome 不是业务接口成功标记,而是网络传输是否完整结束的标记。

6.4 DNS 失败

OkHttp 没有单独的 DNS 失败回调。当前实现通过以下条件判断 DNS 失败:

text 复制代码
dnsStart 已触发 && dnsEnd 未触发 && callFailed 触发

此时:

text 复制代码
dns_status=failed
dns_error=异常类型:异常消息

6.5 网络验证状态变化

当前 NetworkConnectivityMonitor 的上报判断主要基于默认网络对象和网络类型:

java 复制代码
shouldReport = network changed || networkType changed

因此,如果只有 network_validated 从 false 变为 true,网络对象和网络类型都不变,当前实现不会单独上报一次切换事件。

6.6 采集失败不影响业务

接口日志输出、传感器上报和网络切换上报均有异常保护。即使日志字段构造或统计 SDK 调用失败,也不会阻断原始网络请求或改变业务流程。

相关推荐
盟接之桥10 小时前
当大模型遇见线束制造:不是通用AI,而是行业AI
大数据·网络·人工智能·安全·制造
Android系统攻城狮11 小时前
Linux Gstreamer深度解析之gst_audio_encoder_set_frame_max调用流程与实战(六十一)
android·linux·运维·音视频·gstreamer音视频·音视频进阶·gstreamer音视频进阶
91刘仁德11 小时前
IP协议详解:从IP协议头到网段划分、路由与NAT
linux·服务器·网络·网络协议·tcp/ip
可乐鸡翅yeah_12 小时前
业务中 M3U8 水印相关坑,硬水印和动态水印区别
前端·网络·数据库·ffmpeg·m3u8在线
传奇开心果编程14 小时前
【Compose Multiplatform 跨端开发学与练】第7课 平台适配与互操作
android·windows·学习·ui·ios·kotlin·composer
mmsx14 小时前
Android 地图数据链路:从 GDAL、KML 到多引擎适配
android·kotlin
酣大智14 小时前
H3C MSR3600 交换机配置 SSH
网络·ssh·智能路由器
2601_9689007714 小时前
大模型版本回归评估实战:从能力指标到额度口径的完整链路
android·数据挖掘·回归
沐浴露z15 小时前
Agent 响应延迟过高?从工具治理角度提供两条思路
前端·网络
马六六i16 小时前
市面上正规的IP驱动产业新场景新工具有哪些
大数据·网络·python·tcp/ip