Stats API 与监控:getStats() 接口详解、关键指标采集
做实时通信,最怕什么?
不是代码写不出来,而是线上出了问题,你两眼一抹黑。用户说「卡」,你说「我看看」——结果啥也看不到。这就是没有监控的后果。
我个人习惯,在项目初期就把 Stats API 集成进去。别等到用户投诉了再补,那时候就晚了。
getStats() 接口:你的千里眼
WebRTC 的 Stats API,说白了就是一个接口,让你能拿到 PeerConnection 内部的各种统计数据。它不是什么黑魔法,就是浏览器给你开的一扇窗。
调用方式很简单:
const pc = new RTCPeerConnection(configuration);
// 获取所有统计信息
const stats = await pc.getStats();
// 或者指定 track 获取
const senderStats = await pc.getStats(sender);
返回的是一个 Map,key 是字符串 ID,value 是 RTCStats 对象。每个对象都有 type 字段,告诉你这是啥类型的统计。
核心类型一览:
inbound-rtp— 接收端 RTP 统计outbound-rtp— 发送端 RTP 统计candidate-pair— ICE 连接质量remote-inbound-rtp— 远端接收情况track— 音视频轨道的状态
嗯,这里要注意:不同浏览器返回的字段名可能略有差异。我在项目中踩过这个坑,Chrome 和 Safari 的字段命名就不完全一样。所以写代码时最好做一层兼容处理。
关键指标采集:丢包率、抖动、RTT
这三个指标,是衡量 WebRTC 通话质量的「三驾马车」。你想想看,用户说卡,无非就是这三个出了问题。
丢包率(Packet Loss Rate)
丢包率怎么算?不是直接拿到的,得自己算。
// 从 inbound-rtp 中获取
const inboundStats = stats.get(/* inbound-rtp 的 id */);
const packetsLost = inboundStats.packetsLost;
const packetsReceived = inboundStats.packetsReceived;
const totalPackets = packetsLost + packetsReceived;
const lossRate = totalPackets > 0 ? packetsLost / totalPackets : 0;
丢包率超过 5%,用户就能感知到卡顿。超过 10%,基本就没法正常通话了。
我的经验:丢包率要分方向看。上行丢包和下行丢包,原因完全不同。上行丢包通常是发送端网络问题,下行丢包可能是接收端带宽不足。我曾经遇到一个案例,用户一直抱怨听不到声音,结果发现是上行丢包率高达 30%,而他自己完全不知道。
抖动(Jitter)
抖动,就是网络延迟的变化程度。你想想看,如果每个包到达的时间都一样,那很完美。但现实是,有的包快,有的包慢,这就产生了抖动。
// 直接从 inbound-rtp 获取
const jitter = inboundStats.jitter; // 单位:秒
// 注意:Chrome 返回的是秒,Safari 返回的是毫秒
// 建议统一转换为毫秒处理
const jitterMs = jitter * 1000;
抖动值一般要求在 30ms 以下。超过 50ms,用户就会感觉到声音忽快忽慢。
避坑指南:我曾经在集成时发现抖动值忽高忽低,排查了半天,结果是采样周期太短。getStats() 的采样间隔建议至少 1 秒,太频繁反而会引入噪声。
RTT(往返时延)
RTT 就是数据从发送到收到确认的时间。它直接影响通话的实时性。
// 从 candidate-pair 获取
const candidatePair = stats.get(/* candidate-pair 的 id */);
const rtt = candidatePair.currentRoundTripTime; // 单位:秒
// 或者从 remote-inbound-rtp 获取
const remoteStats = stats.get(/* remote-inbound-rtp 的 id */);
const rttFromRemote = remoteStats.roundTripTime;
RTT 在 100ms 以内算优秀,200ms 以内可接受,超过 300ms 就会有明显的延迟感。
| 指标 | 优秀 | 良好 | 较差 |
|---|---|---|---|
| 丢包率 | < 1% | 1% - 5% | > 5% |
| 抖动 | < 20ms | 20ms - 50ms | > 50ms |
| RTT | < 100ms | 100ms - 200ms | > 200ms |
监控系统集成:把数据用起来
光拿到数据没用,得把数据送到监控系统里,才能发挥价值。
我一般会这样做:
- 定时采集:每隔 1-2 秒调用一次 getStats()
- 数据聚合:计算平均值、最大值、最小值
- 上报服务端:通过 WebSocket 或 HTTP 发送到后端
- 可视化展示:在 Grafana 或自建看板上展示
// 一个简单的采集循环
async function collectStats(pc, callback) {
const stats = await pc.getStats();
const report = {};
stats.forEach(stat => {
if (stat.type === 'inbound-rtp' && stat.kind === 'video') {
report.videoInbound = {
packetsLost: stat.packetsLost,
packetsReceived: stat.packetsReceived,
jitter: stat.jitter,
timestamp: stat.timestamp
};
}
// 其他类型同理...
});
callback(report);
setTimeout(() => collectStats(pc, callback), 1000);
}
我的建议:不要把所有原始数据都上报。先做一次聚合,比如 10 秒内的平均值。这样既减少了网络开销,又能平滑掉瞬时波动。我曾经见过有人每秒上报一次,结果监控系统直接被数据淹没了。
知识体系总览
下面这张图,帮你理清 Stats API 的核心脉络:
这张图把整个流程串起来了。从 getStats() 出发,拿到三个核心指标,再汇聚到监控系统。每一步都不复杂,但缺一不可。
总结一下:
- getStats() 是 WebRTC 监控的入口,必须掌握
- 丢包率、抖动、RTT 是三个核心指标,各有各的计算方式和阈值
- 监控系统集成不是可选项,而是必选项
- 数据采集要合理,别太频繁也别太稀疏
好了,这一章就到这里。记住一句话:没有监控的 WebRTC,就像闭着眼睛开车。你永远不知道下一秒会撞上什么。
公众号:蓝海资料掘金营,微信deep3321