如何统计远程Bundle下载的实时带宽与失败率
解读
在国内手游/小程序/数字孪生项目里,首包大小与弱网体验直接决定留存。面试官问“实时带宽与失败率”,不是让你背 API,而是考察:
- 能否在不改 Unity 源码的前提下,零侵入地插桩统计;
- 能否把DNS 劫持、302 跳转、CDN 节点超时、运营商 QoS 限速这些中国特色故障都识别出来;
- 能否把数据实时回传到运营后台,供策划做分包策略、供运维切 CDN。
一句话:既要代码能上线,又要数据能救火。
知识点
- UnityWebRequest 的 SendWebRequest() 异步流程与 DownloadHandler 的 dataFragment 回调;
- Stopwatch 高精度计时与 Application.internetReachability 的运营商网络类型判断;
- DNS 解析耗时 单独拆出来:用 System.Net.Dns.GetHostAddresses 先预解析,避免把解析时间算进下载;
- 失败细分:超时、404、403、证书无效、Content-Length 与实际不符、断点续传失败;
- 实时上报:内存环形队列 + 批量压缩(Deflate)+ 可靠 UDP(KCP 或 QUIC),在弱网 200ms 内把采样点送到 TLOG 或 SLS;
- 采样粒度:每 64 KB 或每 200 ms 取一次瞬时速度,用 滑动指数平均 平滑,防止抖动;
- 合规:工信部 164 号文 要求用户敏感信息不可明文上传,IP 需做 AES-ECB 局部脱敏。
答案
我封装了一个 RemoteBundleProfiler 单例,挂在 Persistent 场景不销毁,流程如下:
-
预解析
下载前先调用 Dns.GetHostAddresses 记录 dnsTime,防止把 DNS 耗时算进带宽。 -
采样
自定义 DownloadHandlerAssetBundle 的子类 InstrumentedHandler,在 ReceiveData 回调里:long nowTicks = Stopwatch.GetTimestamp(); long deltaBytes = dataLength - lastLength; double instantBps = deltaBytes * 10000000.0 / (nowTicks - lastTicks);每 64 KB 或 200 ms 把元组 (instantBps, dnsTime, httpCode, cdnIp, errorType) 压入 ConcurrentQueue。
-
失败判定
在 UnityWebRequest.error 非空或 timeout(国内默认 8 s,海外 12 s)时,根据 responseCode 与 error 文本映射到枚举:- TimeoutDns
- TimeoutTcp
- Http403
- ContentLengthMismatch
- TlsHandshakeFail 并记录 断点续传偏移(request.GetRequestHeader("Range")),用于后续重试策略。
-
实时上报
后台线程每 5 s 或队列长度 ≥ 50 时批量取出,用 MemoryStream + DeflateStream 压缩成 <2 KB payload,通过 KCP 发送到 TLOG 网关,AppSecret + HMAC-SHA256 签名,timestamp + nonce 防重放。 -
运行时可视化
在 Development Build 下,用 IMGUI 画实时折线图:横轴时间,纵轴 Mbps,红色竖线标失败事件,方便 QA 在地铁、电梯复现。
上线三个月后,我们把 CDN 失败率从 2.7% 降到 0.6%,平均下载时长缩短 22%,并且根据带宽分布把 首包拆分阈值从 8 MB 调到 5 MB,次日留存提升 1.4%。
拓展思考
- 如果项目用 Addressables,可以把 ResourceManager.ExceptionHandler 替换成自己的 AsyncOperationBase 装饰器,零脚本入侵地插桩;
- 在 WebGL 平台,UnityWebRequest 底层是 XMLHttpRequest,无法拿到 TCP 级重传,此时可让 CDN 返回 Server-Timing 头,把 cdn-cache-hit、edge-rtt 带回来,作为补充指标;
- 未来做 P2P 分包(如 Unity Cloud Content Delivery 的 Peer Assist),需要把 本机上传带宽 也统计进去,避免上行占满导致下行降速;
- 合规层面,工信部 79 号通报 要求“网络日志保存不少于 6 个月”,因此本地 SQLite 也要留一份脱敏日志,AES-256-CBC + 服务器端公钥 定期归档,防止后台抽查。