最近项目上在做压力测试,im模块在压力测试(200qps 进频道)消息风暴来临的时候会导致浏览器卡死。卡顿勉强能接受,卡死就过分了,于是自己找了下原因:
成员上上下线消息来临的时候,都会执行一次排序,排序完了之后在整体渲染。
当一个群组成员较大,比如5000人的时候,一次排序工作和渲染工作就比较消耗开销,当大量数据到来频繁的处理这个工作内容的时候,浏览器扛不住了,陷入了一种类似死锁的状态。
找到了原因,就直接上对应办法,我想到的方法:
- 分片渲染:大数据整体渲染非常吃开销,所以分段渲染能大大减少开销
- 启动web worker单独线程处理排序任务,不让排序任务阻塞主进程渲染
- 当消息风暴来的时候,会有大量请求,所以我写了一个任务队列异步处理请求,就是处理完上一个再去处理下一个。
- 大量数据来的时候,只有最后一个请求数据是最新的,设置一个定时器,清楚大量不需要的请求,设置阈值,累计40条数据任务只处理最后一条,前面39条全部丢弃。
这样子优化之后,不卡顿了,打开速度飞起,上代码:
/**
- 分块渲染用户数据列表
- */
const container = ref()
const blank = ref()
const list = ref([])
const page = ref(1)
const limit = 20 //一页显示20人
const maxPage = computed(() => Math.ceil(list.value.length / limit))
const showList = computed(() => list.value.slice(0, page.value * limit))
let timer
let perCountValue = 0 //每积累30条处理一次
watch(
...,
async (newValue) => {
//直接处理排序----性能较差,大数据渲染造成页面卡顿
// if (newValue && newValue.length > 0) {
// const resultList =JSON.parse(JSON.stringify(newValue)).sort((a,b)=>{
// return a.memberName.localeCompare(b.memberName)
// })
// list.value = doPai(resultList);
// loading.value = false;
// }
//这里采用web worker异步去处理群组排序工作,单独线程处理大数据计算,不阻塞渲染
// if (newValue && newValue.length > 0) {
// worker.postMessage(JSON.stringify({me:authState.value.mid,val:newValue}));
// }
//消息任务队列&并发阈值控制 来处理消息风暴并发的排序任务-
if (newValue && newValue.length > 0) {
if(perCountValue < 40){//这里就不要clear了
console.log("clear计时器",perCountValue)
timer && clearTimeout(timer)
perCountValue++
timer = setTimeout(() => {
doRunnerWorker(newValue);
}, 300);
}else{
console.log("累计清楚30次请求了,执行一次并清空次数!")
doRunnerWorker(newValue);
}
//验证 排序
// const resultList =JSON.parse(JSON.stringify(newValue)).sort((a,b)=>{
// return a.memberName.localeCompare(b.memberName)
// })
// list.value = doPai(resultList);
// loading.value = false;
}
},
{ immediate: true, deep: true },
);
const doRunnerWorker = (newValue)=>{
runnerWorker.addTask({
fn:() =>new Promise<string>((resolve, _reject) => {
// console.log("ready to use web worker to do list ",{me:authState.value.mid,val:newValue})
worker.postMessage(JSON.stringify({me:authState.value.mid,val:newValue}));
resolve("success");
})
});
perCountValue =0
}
const handleScroll = () => {
if (page.value > maxPage.value) return
const clientHeight = container.value?.clientHeight;
const clientTop = container.value?.getBoundingClientRect().top;
const blankTop = blank.value?.getBoundingClientRect().top;
console.log(clientHeight,clientTop,blankTop,blankTop - clientTop)
if ((blankTop - clientTop) <= clientHeight) {
page.value++
}
}
onMounted(() => {
worker.onmessage = ({ data }) => {
console.log("收到了web worker 处理后的排序数据",data)
runner.addTask({
fn:() =>new Promise<string>((resolve, _reject) => {
window.requestAnimationFrame(()=>{
list.value = data
loading.value = false
resolve("success");
})
})
});
};
});
以上是逻辑核心代码,任务队列代码没晒,网上搜到处都是
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。