16、数据统计与监控:获取Stats API、监控延迟/丢包率/吞吐量、将数据可视化
做WebRTC开发,最怕什么?
我个人最怕的是——用户说“卡”,但你不知道哪里卡。是网络不行?是编码器抽风?还是对端机器扛不住了?
没有数据,你就是在盲猜。所以这一章,咱们来聊聊怎么把WebRTC的“黑盒”打开,把延迟、丢包率、吞吐量这些关键指标抓出来,甚至画成图表。
16.1 核心数据源:RTCPeerConnection的Stats API
WebRTC内置了一套统计接口,叫getStats()。你想想看,浏览器其实一直在默默收集各种网络和媒体数据,只是默认不告诉你。调用这个方法,就能拿到一份“体检报告”。
基本用法很简单:
const pc = new RTCPeerConnection(configuration);
// 每隔2秒拉一次数据
setInterval(async () => {
const stats = await pc.getStats();
stats.forEach(report => {
console.log(report.type, report);
});
}, 2000);
这里返回的report对象,类型非常多。我刚开始接触时也被搞晕了。其实你只需要关注几个核心类型:
| report.type | 含义 | 关键字段 |
|---|---|---|
inbound-rtp |
接收端RTP流(远端→本地) | packetsReceived, packetsLost, jitter, framesDecoded |
outbound-rtp |
发送端RTP流(本地→远端) | packetsSent, bytesSent, retransmittedPacketsSent |
candidate-pair |
当前使用的网络连接 | currentRoundTripTime, availableOutgoingBitrate, state |
transport |
传输层信息 | bytesSent, bytesReceived, dtlsState |
type过滤,只处理你关心的那几种。否则数据量太大,前端渲染会卡。
16.2 三个核心指标:延迟、丢包率、吞吐量
数据拿到了,怎么算?我直接给你公式和代码。
16.2.1 延迟(RTT)
延迟就是数据从A到B再回来的时间。在candidate-pair里,有个字段叫currentRoundTripTime,单位是秒。
function getRTT(statsMap) {
let rtt = 0;
statsMap.forEach(report => {
if (report.type === 'candidate-pair' && report.state === 'succeeded') {
rtt = report.currentRoundTripTime * 1000; // 转成毫秒
}
});
return rtt;
}
嗯,这里要注意:currentRoundTripTime是平滑后的值,不是瞬时值。如果你想要更灵敏的监控,可以用totalRoundTripTime和responsesReceived自己算平均值。
16.2.2 丢包率
丢包率是衡量网络质量的关键。在inbound-rtp里,有packetsLost和packetsReceived。
function getPacketLoss(statsMap) {
let lossRate = 0;
statsMap.forEach(report => {
if (report.type === 'inbound-rtp' && report.kind === 'video') {
const total = report.packetsLost + report.packetsReceived;
if (total > 0) {
lossRate = (report.packetsLost / total) * 100;
}
}
});
return lossRate; // 百分比
}
packetsLost是累计值。如果你直接拿它做除法,得到的是“从连接开始到现在的总丢包率”。这其实没什么用。正确的做法是:每次采样时,记录上一次的值,然后计算差值。
改进后的代码:
let prevStats = { packetsLost: 0, packetsReceived: 0 };
function getDeltaPacketLoss(currentStats) {
const lostDelta = currentStats.packetsLost - prevStats.packetsLost;
const totalDelta = (currentStats.packetsLost + currentStats.packetsReceived)
- (prevStats.packetsLost + prevStats.packetsReceived);
prevStats = {
packetsLost: currentStats.packetsLost,
packetsReceived: currentStats.packetsReceived
};
if (totalDelta === 0) return 0;
return (lostDelta / totalDelta) * 100;
}
16.2.3 吞吐量(比特率)
吞吐量就是每秒传输了多少数据。这个需要自己算差值。
let lastBytes = 0;
let lastTimestamp = 0;
function getBitrate(currentBytes, currentTimestamp) {
if (lastTimestamp === 0) {
lastBytes = currentBytes;
lastTimestamp = currentTimestamp;
return 0;
}
const deltaBytes = currentBytes - lastBytes;
const deltaTime = (currentTimestamp - lastTimestamp) / 1000; // 转成秒
lastBytes = currentBytes;
lastTimestamp = currentTimestamp;
return (deltaBytes * 8) / deltaTime; // 转成bps
}
你可以在outbound-rtp的bytesSent,或者inbound-rtp的bytesReceived里拿到字节数。
16.3 数据可视化:把数字变成图表
光看数字太枯燥了。我个人习惯用Canvas或者SVG画实时折线图。这里我用一个轻量方案——纯SVG绘制。
下面这张图展示了我们监控系统的核心流程:
实际画图时,我推荐用<polyline>来画折线。每次采样后,把新数据点push到数组里,然后更新路径的points属性。
// 简化版:用SVG画实时折线图
const svgNS = "http://www.w3.org/2000/svg";
const polyline = document.createElementNS(svgNS, "polyline");
polyline.setAttribute("stroke", "#4A90D9");
polyline.setAttribute("stroke-width", "2");
polyline.setAttribute("fill", "none");
svgElement.appendChild(polyline);
let dataPoints = [];
const MAX_POINTS = 50; // 只保留最近50个点
function updateChart(value) {
dataPoints.push(value);
if (dataPoints.length > MAX_POINTS) {
dataPoints.shift();
}
// 将数据映射到SVG坐标
const points = dataPoints.map((v, i) => {
const x = i * (800 / MAX_POINTS);
const y = 200 - (v / 100) * 180; // 假设值范围0-100
return `${x},${y}`;
}).join(" ");
polyline.setAttribute("points", points);
}
polyline。复用同一个元素,只更新points属性,性能会好很多。我试过每秒更新20次,CPU占用几乎没变化。
16.4 避坑指南与最佳实践
最后,分享几个我实际项目中总结的经验:
- 采样频率别太高。 每秒1-2次就够了。你想想看,网络变化没那么快,频繁采样反而增加浏览器负担。
- 注意跨域问题。 如果你在Worker里调用
getStats(),记得把RTCPeerConnection传进去。Worker里不能直接创建PeerConnection。 - 数据要平滑。 原始数据抖动很大。我习惯用移动平均(Moving Average)做平滑处理,比如取最近3次采样的平均值。
- 别忘了清理定时器。 页面关闭或连接断开时,记得
clearInterval。否则定时器会一直跑,造成内存泄漏。
核心要点回顾:
- 用
getStats()获取原始数据,按type过滤 - 丢包率一定要算差值,不要用累计值
- 吞吐量用字节差除以时间差,再乘以8转成bps
- 可视化用SVG的
polyline,复用元素提升性能 - 采样频率1-2Hz,数据做平滑处理
数据统计这块,说白了就是“把看不见的网络状况,变成看得见的数字和图表”。你把这些工具用好,以后用户再反馈卡顿,你就能直接甩出一张监控截图:“你看,当时丢包率飙到15%了,是网络问题,不是我的代码问题。”——嗯,这种感觉还是挺爽的。
公众号:蓝海资料掘金营,微信deep3321