网络请求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 调用失败,也不会阻断原始网络请求或改变业务流程。

相关推荐
青瓦梦滋9 小时前
WebSocket协议
linux·服务器·网络·c++·websocket·网络协议·http
zhang1338308907510 小时前
传统机械三杯式风速传感器易磨损冻堵?CG-88金属超声波风速风向传感器给出免运维方案
运维·服务器·网络·人工智能·自动化
知已难寻10 小时前
第 2 篇 · 云的地基:服务器、存储、网络三大基础设施
运维·服务器·网络·云原生·云计算·负载均衡·1024程序员节
IpdataCloud12 小时前
批量查IP的API接口怎么选?免费API配额瓶颈与离线库本地批量方案实测
网络·网络协议·tcp/ip
警醒与鞭策12 小时前
Codex桌面版与Claude桌面版使用设置
android·unity·ai·性能优化
ITyunwei098713 小时前
售后夜间值守怎么落地:从告警到交接的工单系统配置笔记
网络
Smoothcloud润云14 小时前
GPU远程纳管安全吗?最小权限、网络边界与日志审计实战
服务器·网络·人工智能·安全·gpu算力·gpu
ai大模型-探索者14 小时前
Function Call与MCP(区别、联系与实战)
java·服务器·网络
木子欢儿14 小时前
Debian 13 + vsftpd 搭建FTP服务
linux·服务器·前端·网络·debian
达~熊迪15 小时前
IP 分配前核查:把在线状态和使用登记一起看
服务器·网络·tcp/ip