JS 實現網絡測速

一、背景知識

在我們的日常生活中離不開網絡,而網絡的快慢直接決定了用戶的產品使用體驗。最近我們的 WMS 系統在倉庫使用過程中出現了網絡卡頓導致的異常情況,因此需要提供一個網絡檢測功能,當倉庫再遇到類似問題時可以先通過測量網速來排查是否網絡出現了異常。

名詞解釋:

  1. ping:給目標 IP 地址發送一個 ICMP 報文,再要求對方返回一個大小相同的數據包來確定兩臺網絡機器是否能正常通信以及有多少時延。我們 ping 一下 github 試試:這裏的 time 指標就是時延數值。ping 可以作爲網絡情況的首要參考指標;

  2. jitter:抖動,用來描述網絡的波動情況。比如每秒測量一次 ping 值,5s 後取五次測量結果的最大最小值求差,可以看出網絡的波動情況,差值越小代表網絡越穩定;

  3. bandwidth:帶寬,用來描述理論上單位時間內網絡傳輸數據的最高速率,它只是一個理論上的最大值。通常我們所說的 1 兆帶寬就是 1Mb/s = 1000Kb/s = (1000/8)KB/s = 125KB/s。帶寬越大自然越好,但是受用戶計算機性能、網絡設備質量、資源使用情況、網絡高峯期、網站服務能力、線路衰耗,信號衰減等多因素的影響,它並不能直接反映當前的網絡環境;

  4. throughput:吞吐量,用來描述單位時間內網絡傳輸數據的實際速率。受多方面影響,吞吐量一般都小於真正的帶寬值;

  5. 上傳速度與下載速度:上傳速度就是從本地上傳一個文件的速度,相反,下載速度就是從網絡上下載一個文件的速度,使用迅雷等下載軟件的時候看到的那個數值就是下載速度,通常下載速度會大於上傳速度。由於下載場景較多,我們更關心下載速度數值。

在網絡檢測中,沒有單獨哪一個指標可以說明問題,應儘可能多的結合各種指標來全面評估網絡情況。接下來介紹幾種測速方法。

二、Network Information API

瀏覽器爲我們提供了網絡相關的 API ,NetworkInformation 提供了設備與網絡進行通信的信息和鏈接類型變更時的有關事件,它是通過 Navigatorconnection 屬性進行訪問的。connection 對象有一個 downlink 屬性,返回以 Mb/s 爲單位的有效帶寬,MDN 上官方解釋說該值是基於最近監測的保持活躍連接的應用層吞吐量,因此吞吐量的查詢並不是實時的,如果距離上一個 http 請求間隔較長,這個數值並不準確。因此 downlink 值只具備有限的參考意義,且該功能還在實驗中,不同瀏覽器兼容性也較差,因此不推薦使用這種方式來檢測網絡情況。

補充一下,connection 對象還有一個 type 屬性和 onChange 方法,type 屬性返回的是當前設備聯網的類型,枚舉值有如下幾種:

當聯網類型 type 發生改變時,會觸發 change 事件,通過 onChange 回調函數能做一些事情:比如我們在播放視頻時,從 wifi 環境切換爲使用流量時,可以暫停視頻並提示用戶選擇是否用流量繼續播放。

三、測量 ping 和 jitter 值

由於 JS 無法真正原生地測量 ping 值,因此需要提供一種替代方案來模擬。爲了儘可能準確地得到 ping 值,可以通過請求一個儘量小的資源來模擬發送 ICMP 報文,記錄發起請求到收到返回值的時間差。請求的內容可以是網站的 favicon.ico ,一個空文件,甚至是一個空接口(注意需要配置跨域)。但是這些方案都依賴於圖片資源、文件以及接口的穩定性,如果服務掛掉的話,得到的 ping 值是有問題的。接下來通過多次測量 ping 值就可以計算代表網絡波動情況的 jitter 值了。代碼如下:

const Dashboard = React.memo(() ={
  const [ping, setPing] = useState<number>(0);
  const [count, setCount] = useState<number>(0);
  const [pingList, setPingList] = useState<number[]>([]);
  const [jitter, setJitter] = useState<number>(0);

  useEffect(() ={
    const timer = setInterval(() ={
      const img = new Image();
      const startTime = new Date().getTime();
      // 此處選擇加載 github 的 favicon,大小爲2.2kB
      img.src = `https://github.com/favicon.ico?d=${startTime}`;
      img.onload = () ={
        const endTime = new Date().getTime();
        const delta = endTime - startTime;
        if ((count + 1) % 5 === 0) {
          const maxPing = Math.max(delta, ...pingList);
          const minPing = Math.min(delta, ...pingList);
          setJitter(maxPing - minPing);
          setPingList([]);
        } else {
          setPingList(lastList =[...lastList, delta]);
        }
        setCount(count + 1);
        setPing(delta);
      };
      img.onerror = err ={
        console.log('error', err);
      };
    }, 3000);
    return () => clearInterval(timer);
  }[count, pingList]);

  return (
    <PageContainer className={styles.dashboard}>
      <div class>
        <h1>歡迎使用 倉儲管理系統</h1>
        <h1>PING: {ping}ms</h1>
        <h1>抖動: {jitter}ms</h1>
      </div>
    </PageContainer>
  );
});

以上代碼是採用測量加載 github favicon 的時間來模擬 ping 值的,圖片大小爲 2.2kB,可以獲得更準確的 ping 值。注意在 img.src 的 url 最後拼接上一個時間戳,保證每次都會重新發起請求,而不是使用第一次加載的圖片緩存。動圖效果如下,3 秒測量一次 ping 值,拿到最近 5 次 ping 值後計算一次抖動值:

四、測量下載速度

下載速度測量與上述 ping 值測量原理相同,只不過需要將下載的對象換成一個更大的資源,通過計算單位時間內下載資源的大小來測量下載速度。像下載軟件迅雷,我們常看到的那個數字就是下載速度。另外迅雷還有一個很好的功能可以選擇全速下載模式和不影響正常上網的模式,因爲下載時可能會擠佔帶寬影響用戶正常瀏覽網頁。其中的原理就是迅雷在下載的時候在不停做 ping,如果發現 ping 的延遲增加,就限制下載速度,如果 ping 還高,就繼續降到 ping 迴歸期望值。

五、總結

如果用戶感到訪問的網站反應過慢,有可能是各種原因導致的,大致可以遵循以下流程進行簡單排查:

  1. 打開百度等常用網站,查看是否仍然存在網速慢的情況。如果其他網站訪問正常,可以確定是當前站點的問題,需要繼續排查:
  1. 如果其他網站速度也比較慢,可以檢查是否在下載文件,如果在下載文件也是會佔用帶寬的,可以選擇下載軟件的限制帶寬功能來確保正常上網網速;

  2. 如果確實網絡有問題,那就需要運營商維修人員來排查了,有可能是各種原因:用戶計算機性能、網絡設備質量、網絡高峯期、線路衰耗、信號衰減等等。

注意事項:

  1. 不要在頁面加載的初始階段就去測速,否則會影響 LCP 時間,建議等組件 mounted 後再測速;

  2. 根據業務場景合理設計測速方案,比如根據測速時機的不同分爲兩種方案:

  1. 若使用加載圖片的方式測量,圖片 url 應拼接時間戳,防止請求時直接使用緩存。

參考資料

[1]

Network Information API - Web APIs | MDN: https://developer.mozilla.org/en-US/docs/Web/API/Network_Information_API

[2]

每天都在用的 Ping 命令,它到底是什麼?: https://zhuanlan.zhihu.com/p/45110873

[3]

在前端 Network 還能這樣玩: https://segmentfault.com/a/1190000021894327

[4]

探索瀏覽器端的網絡速度測試: https://segmentfault.com/a/1190000038589149

[5]

https://github.com/penghuwan/network-speed-test: https://github.com/penghuwan/network-speed-test

[6]

GitHub - alfg/ping.js: Ping the web with Javascript.: https://github.com/alfg/ping.js#notes

[7]

https://netcare.net/tools/network: https://netcare.net/tools/network

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/3DxzntvcNt08XU8MNjmBeA