最近项目上在做压力测试,im模块在压力测试(200qps 进频道)消息风暴来临的时候会导致浏览器卡死。卡顿勉强能接受,卡死就过分了,于是自己找了下原因:

成员上上下线消息来临的时候,都会执行一次排序,排序完了之后在整体渲染。

当一个群组成员较大,比如5000人的时候,一次排序工作和渲染工作就比较消耗开销,当大量数据到来频繁的处理这个工作内容的时候,浏览器扛不住了,陷入了一种类似死锁的状态。
找到了原因,就直接上对应办法,我想到的方法:

  1. 分片渲染:大数据整体渲染非常吃开销,所以分段渲染能大大减少开销
  2. 启动web worker单独线程处理排序任务,不让排序任务阻塞主进程渲染
  3. 当消息风暴来的时候,会有大量请求,所以我写了一个任务队列异步处理请求,就是处理完上一个再去处理下一个。
  4. 大量数据来的时候,只有最后一个请求数据是最新的,设置一个定时器,清楚大量不需要的请求,设置阈值,累计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");
    })
         
  })
});

};
});

以上是逻辑核心代码,任务队列代码没晒,网上搜到处都是


罗鼎
57 声望4 粉丝