小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
背景
近日在浏览文章,看到一篇【动态性能分级策略在客户端的实践】的文章,里面讲述了如何利用收集的设备的信息,如内存、CPU、电量、网速信息进行性能等级判断,业务组件再根据性能等级分析去执行对应的性能策略方案,比如:当你使用oss服务存储图片资源,你可以在整体性能等级优的情况下,去动态修改oss参数,返回更高分辨率的图片,反之降低图片分辨率。
本文整理下,在浏览器环境中如何去监控对应的数据。
系统监控
1. CPU监控
CPU在浏览器中很少被提及,很少有这个概念,往往提及的是js任务执行延迟,在往里细挖就是浏览器的事件循环系统、消息队列、宏任务、微任务、延迟队列等等了。当前面任务发生了阻塞,比如执行了耗时任务,那必然导致后续事件延迟执行了,毕竟js执行是单线程的。Web Api也没有提供获取CPU的方式,那我们怎么获取呢?换句话说,如何计算出单位时间内,js任务执行延迟时间,这也可以反应出当前js执行效率,这也是我们想要的。
看这段代码:
let data = [],t;
let getNow = ()=>new Date().getTime()
const cpuTimer = setInterval(()=>{
t && data.push(getNow()-t);
t = getNow();
},500);
理想情况下例子输出的 data值应该是[500,505,508,506,...]在某个范围值内波动,当波动较小时,可以认为当前js任务执行效率高、无存在阻塞的js任务。但是当波动阀值超过,比如:50,我们可以认定存在执行耗能的js任务。
2. 内存监控
Performance提供了对当前页面性能访问的相关信息,比如:Performance Timeline API/ Navigation Timing API/ User Timing API等。在Chrome中添加了一个非标准扩展:Performance.memory,它提供了内存使用的信息。
const {
// 总分配的堆大小,单位字节
totalJSHeapSize,
// 当前使用的js堆,单位字节
usedJSHeapSize,
// 上下文可用的最大堆大小,单位字节
jsHeapSizeLimit} = performance.memory
关于用法,你可以监控浏览器使用内存的阀值,超出则告警:
(()=>{
// 允许最大50MB
const MAX_MEMORY_ALLOW = 50 * 1048576; // 50MB
// 最大使用率90%
const MAX_MEMORY_PERCENT_ALLOW = 90;
requestAnimationFrame(function monitor(){
if(performance.memory.usedJSHeapSize > MAX_MEMORY_ALLOW){
// 高警:使用超过50MB
}
if(performance.memory.usedJSHeapSize > (MAX_MEMORY_PERCENT_ALLOW/100)*performance.memory.jsHeapSizeLimit){
// 告警:使用率超过90%
}
})
})()
3. 网络监控
网络监控是为了获取当前时间片段的网速,当网速好,响应更高分辨率的资源图片。Web API也没有提供对应的能力。那获取时间段网络速度,可以通过间断时间,从服务端加载资源文件,计算网速。
function getNetworkSpeed(){
const startTime = getNow();
const xhr = new XMLHttpRequest()
xhr.open('GET', '服务端资源文件,建议给个5~10M的资源图片')
xhr.onload = function(){
// 下载时间
const duration = (getNow() - startTime)/1000;
// 转为M
const size = xhr.getResponseHeader('Content-Length')/1024/1024;
// 下载速度: * Mb/s
const speed = (size / duration).toFixed(2)
}
xhr.send()
}
4. 电量监控
电量监控主要是为了获取设备低电量状态,在系统电量不足的时候降低一些循环执行任务的频率,从而节约电量。或者在电量减少到某个级别的时候,自动保存页面上的一些数据,以防止用户数据丢失。可以有效降低用户电量焦虑。
Web API 提供了Battery Status API,它提供了2个接口:
- BatteryManager:提供有关系统电池电量水平的信息
- navigator.getBattery():返回一个Promise对象,resolve 参数是 BatteryManager对象
下面看看用法
function updateBatteryStatus(battery) {
// 布尔值,表示电池的充电状态
document.querySelector('#charging').textContent = battery.charging ? 'charging' : 'not charging';
// 表示电池的电量等级,从0到1
document.querySelector('#level').textContent = battery.level;
// 表示电量还剩多久时间会消耗完,单位(s)
document.querySelector('#dischargingTime').textContent = battery.dischargingTime / 60;
}
navigator.getBattery().then(function(battery) {
// Update the battery status initially when the promise resolves ...
updateBatteryStatus(battery);
// .. and for any subsequent updates.
battery.onchargingchange = function () {
updateBatteryStatus(battery);
};
battery.onlevelchange = function () {
updateBatteryStatus(battery);
};
battery.ondischargingtimechange = function () {
updateBatteryStatus(battery);
};
});
使用此API需要慎重,因为它的兼容性并不好,并且Battery Status API在未来从Web 标准中删除。 兼容性:
总结
系统环境监控在浏览器中由于存在的兼容性,或者web应用在性能颗粒度上有时候要求并没有这么苛刻,因此此类场景使用上比较少,权当做个了解,扩展下视野。