狠狠色丁香婷婷综合尤物/久久精品综合一区二区三区/中国有色金属学报/国产日韩欧美在线观看 - 国产一区二区三区四区五区tv

LOGO OA教程 ERP教程 模切知識交流 PMS教程 CRM教程 開發文檔 其他文檔  
 
網站管理員

瀏覽器節能機制導致Websocket斷連的坑

freeflydom
2024年4月29日 16:20 本文熱度 1159

序言

你踩過嗎?瀏覽器節能機制導致Websocket斷連的坑~~~

近期,在使用WebSocket(WS)連接時遇到了頻繁斷連的問題,這種情況在單個用戶上每天發生數百次。盡管利用了socket.io的自動重連機制能夠在斷連后迅速恢復連接,但這并不保證每一次重連都能成功接收WS消息。因此,我們進行了一些的排查和測試工作。

最終發現問題的根本原因:正是瀏覽器的節能機制,不經意間成為了這一問題的幕后黑手

瀏覽器節能機制簡介

瀏覽器的節能機制逐漸成為前端開發者需要關注的問題。特別是這些節能機制可能會對定時器的精度產生影響,這直接關系到前端應用的用戶體驗,在某些場景下甚至影響到用戶的使用。

為了減少電能消耗,提高電池續航能力,現代瀏覽器都引入了節能機制。這些機制包括但不限于降低空閑標簽頁的CPU使用率、減少后臺JavaScript的執行頻率、限制定時器的精確度等。雖然這些措施顯著提高了設備的能效,但也給前端開發帶來了一些挑戰。

WS頻繁斷連原因分析

查閱socket.io官網服務端配置的pingTimeoutpingInterval兩個參數發現WS心跳異常時會導致重連,具體說明:

WS連接中服務端和客戶端兩端必須一直保持心跳。如果有一端停止,則滿足如下條件之一就會自動斷連:

  • 服務器發送 ping,如果客戶端在毫秒內 pingTimeout 沒有用 pong 應答,則服務器認為連接已關閉。

  • 同樣,如果客戶端在毫秒內 pingInterval + pingTimeout 未收到來自服務器的 ping,則客戶端也會認為連接已關閉。

看文檔發現其實高版本的socket.io是由服務端定時發起ping。而在socket.io 2.X的版本中內置的心跳機制是由客戶端定時發起。而瀏覽器在后臺運行時,即使你設置了一個每秒觸發的定時器,它也只能每分鐘觸發一次,超過了pingInterval + pingTimeout設置的時間,最后看到的日志是很有規律的每分鐘重連一次。在之前寫的這篇文章中也有相關的介紹《掌握Web Workers:徹底解鎖前端多線程編程的潛力》

WS頻繁斷連解決方法

@升級socket.io到最新版本

上面的截圖其實就是最新版本(4.x)的,升級后由服務器定時發起心跳。在服務端定時運行,避開了瀏覽器節能機制對定時器的影響

@自定義WS心跳事件

為了減小直接升級對已有業務的影響,目前使用的也是這種方案:在服務端自定義心跳事件,定時發送心跳custom-ping

// 客戶端的CODE

io.on('custom-ping', function () {

  io.emit('custom-pong', Date.now())

})


// 服務端CODE

io.on('connection', (socket) => {

  console.log('New client connected');


  // 發送自定義ping消息

  const pingInterval = setInterval(() => {

    socket.emit('custom-ping', Date.now());

  }, 10000); // 每10秒發送一次


  // 監聽自定義pong消息

  socket.on('custom-pong', (data) => {

    console.log('Pong received:', data);

  });


  socket.on('disconnect', () => {

    clearInterval(pingInterval);

    console.log('Client disconnected');

  });

});


注意:斷連時一定要銷毀定時器

其實,socket.io是有內置心跳的(2.x版本客戶端定時發起,4.x由服務端定時發起),自定義心跳的意義主要在于保持數據交換,在這個時間間隔內保持數據交換,socket就不會自動中斷重連。

@使用setTimeout

這里要注意使用setTimeout的姿勢,如果是直接這樣使用、依然會有精度問題。

setTimeout丟失精度的情況:

// 以下setTimeout仍然會丟失精度

let _cacheTs = Date.now()

const _setTimeoutFn = () => {

  console.log('setTimeout :>> ', Date.now() - _cacheTs);

  _cacheTs = Date.now()

  setTimeout(() => {

    _setTimeoutFn()

  }, 5000)

}

_setTimeoutFn()

在setTimeout里面去執行一個函數棧會被瀏覽器監控到,會認為和setInterval一樣,其在后臺運行時會降低其定時精度。  但如果這樣可以避開節能機制的限制:

setTimeout不丟失精度的情況:

// 客戶端CODE

// 監聽服務端發送的custom-pong事件

socket.on('custom-pong', onHeart)


const onHeart = () => {

  if (timer) {

    clearTimeout(pingTime.current)

  }

  timer = window.setTimeout(() => {

    socket.emit('custom-ping', Date.now())

  }, 5000)

}


// 服務端CODE

socket.on('custom-ping', ()=>{

  socket.emit('custom-pong', Date.now())

})

@使用Web-Workers

在Web-Workers線程內發起定時不受瀏覽器節能機制的限制,相關示例在這篇文章里也有介紹《掌握Web Workers:徹底解鎖前端多線程編程的潛力》

@頁面保活(實測無效)

在后臺運行時也保持瀏覽器的活躍,用得最多的方式是在頁面隱藏一個循環播放的音頻 或者 使用nosleep.js


const noSleepInstance = new NoSleep();

document.addEventListener('click', function enableNoSleep() {

  document.removeEventListener('click', enableNoSleep, false);

  noSleepInstance.enable();

}, false);


實測,使用這種方式時,瀏覽器在后臺運行仍然存在定時器精度降低的問題

小結

WS頻繁斷連的原因:

  1. 使用了低版本(2.x)的socket.io

  2. 客戶端每5秒定時發送 心跳

  3. 瀏覽器后臺運行時觸發節能機制限制了定時器的精度,由每5秒變成了實際的每分鐘執行一次

  4. 每分鐘執行一次遠大于socket.io設置的pingTimeout時間

  5. WS斷開連接

  6. socket.io內置的重連機制,立即重連成功

  7. 查看日志發現每分鐘重連一次。
    在實際排查中,是從第七步倒退排查發現是瀏覽器節能機制所引起的問題。。。

總結


作者:tager
鏈接:https://juejin.cn/post/7362576319928008755
來源:稀土掘金
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。



該文章在 2024/4/29 16:20:37 編輯過
關鍵字查詢
相關文章
正在查詢...
點晴ERP是一款針對中小制造業的專業生產管理軟件系統,系統成熟度和易用性得到了國內大量中小企業的青睞。
點晴PMS碼頭管理系統主要針對港口碼頭集裝箱與散貨日常運作、調度、堆場、車隊、財務費用、相關報表等業務管理,結合碼頭的業務特點,圍繞調度、堆場作業而開發的。集技術的先進性、管理的有效性于一體,是物流碼頭及其他港口類企業的高效ERP管理信息系統。
點晴WMS倉儲管理系統提供了貨物產品管理,銷售管理,采購管理,倉儲管理,倉庫管理,保質期管理,貨位管理,庫位管理,生產管理,WMS管理系統,標簽打印,條形碼,二維碼管理,批號管理軟件。
點晴免費OA是一款軟件和通用服務都免費,不限功能、不限時間、不限用戶的免費OA協同辦公管理系統。
Copyright 2010-2025 ClickSun All Rights Reserved