JavaScript 是如何工做的:深刻網絡層 + 如何優化性能和安全

阿里雲最近在作活動,低至2折,有興趣能夠看看:
https://promotion.aliyun.com/...

爲了保證的可讀性,本文采用意譯而非直譯。javascript

JavaScript 是如何工做的:深刻網絡層 + 如何優化性能和安全html

這是專門探索 JavaScript 及其所構建的組件的系列文章的第 12 篇。前端

若是你錯過了前面的章節,能夠在這裏找到它們:java

  1. JavaScript 是如何工做的:引擎,運行時和調用堆棧的概述!
  2. JavaScript 是如何工做的:深刻V8引擎&編寫優化代碼的5個技巧!
  3. JavaScript 是如何工做的:內存管理+如何處理4個常見的內存泄漏 !
  4. JavaScript 是如何工做的:事件循環和異步編程的崛起+ 5種使用 async/await 更好地編碼方式!
  5. JavaScript 是如何工做的:深刻探索 websocket 和HTTP/2與SSE +如何選擇正確的路徑!
  6. JavaScript 是如何工做的:與 WebAssembly比較 及其使用場景 !
  7. JavaScript 是如何工做的:Web Workers的構建塊+ 5個使用他們的場景!
  8. JavaScript 是如何工做的:Service Worker 的生命週期及使用場景!
  9. JavaScript 是如何工做的:Web 推送通知的機制!
  10. JavaScript是如何工做的:使用 MutationObserver 跟蹤 DOM 的變化
  11. JavaScript是如何工做的:渲染引擎和優化其性能的技巧

正如在上一篇關於 渲染引擎 的博客文章中提到的,咱們認爲優秀的 JavaScript 開發人員和傑出的 JavaScript 開發人員之間的區別在於,後者不只理解語言的具體細節,並且理解其內部結構和周遭環境。git

想閱讀更多優質文章請猛戳GitHub博客,一年百來篇優質文章等着你!github

講一點歷史

49年前,一種叫作 ARPAnet 的網誕生了。它是一個早期的 分組交換網絡,也是第一個 實現TCP/IP套件的網絡。20年後,蒂姆·伯納斯-李提出了一種「網狀結構」的建議,這種結構後來被稱爲「萬維網」。在這 49 年裏,互聯網走過了漫長的道路,從僅僅兩臺計算機交換數據包,到超過 7500 萬臺服務器、38 億互聯網用戶和 13 億個網站。web

"阿帕"(ARPA),是美國高級研究計劃署(Advanced Research ProjectAgency)的簡稱。他的核心機構之一是信息處理(IPTO Information Processing Techniques Office),一直在關注電腦圖形、網絡通信、超級計算機等研究課題。

阿帕網爲美國國防部高級研究計劃署開發的世界上第一個運營的封包交換網絡,它是全球互聯網的始祖。chrome

圖片描述

在這篇文章中,咱們將嘗試分析現代瀏覽器使用什麼技術來自動提升性能(甚至在你不知道的狀況下),接着深刻瀏覽器網絡層。最後,咱們將提供一些關於如何幫助瀏覽器提升 Web 應用程序性能的建議。編程

概覽

現代 Web 瀏覽器專爲快速,高效,安全地提供網絡應用/網站而設計。 數百個組件在不一樣的層上運行,從流程管理和安全沙箱到 GPU 管道,音頻和視頻等等,Web 瀏覽器看起來更像是一個操做系統,而不只僅是一個軟件應用程序。segmentfault

瀏覽器的整體性能由許多大型組件決定:解析、佈局、樣式計算、JavaScript 和 WebAssembly 執行、渲染,固然還有網絡堆棧。

工程師常常認爲網絡堆棧是一個瓶頸。這種狀況常常發生,由於全部資源都須要從網上獲取,而後才能解除其他步驟的阻塞。爲了使網絡層高效,它須要扮演的角色不只僅是一個簡單的套接字管理器。它提供給咱們的是一種很是簡單的資源獲取機制,但實際上它是一個具備本身的優化標準、API 和服務的完整平臺。

圖片描述

做爲 Web 開發人員,咱們沒必要擔憂單獨的 TCP 或 UDP 數據包、請求格式化、緩存和其餘一切問題。整個複雜性由瀏覽器負責,所以咱們能夠將精力集中在咱們正在開發的應用程序上。然而,瞭解底層的狀況能夠幫助咱們建立更快、更安全的應用程序。

本質上,當用戶開始與瀏覽器交互時會發生如下狀況:

  • 用戶在瀏覽器地址欄中輸入一個 URL
  • 給定 Web 上資源的 URL,瀏覽器首先檢查其本地緩存和應用程序緩存,並嘗試使用本地副原本完成請求
  • 若是緩存不能使用,瀏覽器從 URL 獲取域名,並從 DNS 請求服務器的 IP 地址。若是域被緩存,則不須要 DNS 查詢
  • 瀏覽器建立一個 HTTP 包,表示它請求位於遠程服務器上的 Web 頁面
  • 數據包被髮送到 TCP 層,TCP 層在 HTTP 數據包上添加本身的信息,維護已啓動的會話須要此信息
  • 而後數據包被傳遞給 IP 層,IP 層的主要任務是找出一種將數據包從用戶發送到遠程服務器的方法,這些信息也存儲在包的頂部
  • 數據包被髮送到遠程服務器
  • 一遠程服務器一旦接收到數據包,就會以相似的方式發回響應。

W3C的瀏覽時序規範(Navigation Timing specification)提供了一個瀏覽器API,讓咱們能夠看到瀏覽器中每項請求的生命週期背後的時序和性能數據。讓咱們看看這些組成部分,每一塊都是影響最佳用戶體驗的關鍵點:

圖片描述

整個網絡過程很是複雜,有許多不一樣的層,這可能成爲瓶頸。這就是爲何瀏覽器努力經過使用各類技術來提升本身的性能,從而使整個網絡通訊的影響最小。

套接字管理

先了解一些術語:

  • 源(Origin) - 由應用程序協議,域名和端口號組成(例如https,www.example.com,443)
  • 套接字池(Socket pool) - 屬於同一源的一組套接字(全部主要瀏覽器將最大池大小限制爲6個套接字)

JavaScript 和 WebAssembly 不容許咱們管理單個網絡套接字的生命週期,這是一件好事!這不只使咱們的省去較多麻煩,並且還可讓瀏覽器自動進行許多性能優化,其中包括套接字重用、請求優先級和後期綁定、協議協商、強制鏈接限制等。

實際上,現代瀏覽器在將請求管理週期與套接字管理分離方面作了更多的工做。套接字組織在按源分組的池中,每一個池執行本身的鏈接限制和安全約束。掛起的請求被排隊、排序,而後綁定到池中的各個套接字。除非服務器有意關閉鏈接,不然同一個套接字能夠跨多個請求自動重用!

圖片描述

因爲打開新的 TCP 鏈接須要額外的成本,所以鏈接的重用自己就帶來了巨大的性能優點。默認狀況下,瀏覽器使用所謂的 「keepalive」 機制,它能夠在發出請求時節省打開到服務器的新鏈接的時間。打開新 TCP 鏈接的平均時間爲:

  • 當地的請求  — 23ms
  • 橫貫大陸的請求 —— 120ms
  • 洲際請求 ——  225ms

這種架構爲其餘一些優化提供了可能, 請求能夠根據其優先級以不一樣的順序執行。 瀏覽器能夠優化全部套接字的帶寬分配,也能夠在預期請求時打開套接字。

正如以前提到的,這一切都由瀏覽器管理,不須要咱們作任何工做,但這並不意味着咱們什麼都作不了。 選擇正確的網絡通訊模式,類型和傳輸頻率,協議選擇以及服務器堆棧的調優/優化能夠在提升應用程序的總體性能方面發揮重要做用。

有些瀏覽器甚至更進了一步。 例如,Chrome 能夠學習用戶的操做習慣來使本身變得更快。 它根據訪問的站點和典型的瀏覽模式進行學習,以便預測可能的用戶行爲並在用戶執行任何操做以前採起措施。 最簡單的例子是當用戶在連接上懸停時,Chrome 會預先渲染頁面, 若是有興趣瞭解有關 Chrome 優化的更多信息,能夠查看這篇文章 https://www.igvita.com/posa/h...

網絡安全和沙盒

容許瀏覽器管理單個套接字還有另外一個很是重要的目的:經過這種方式,瀏覽器可以對不受信任的應用程序資源執行一致的安全和策略約束。例如,瀏覽器不容許 API 直接訪問原始網絡套接字,由於這將使任何惡意應用程序可以任意鏈接到任何主機。瀏覽器還強制執行鏈接限制,以保護服務器和客戶端免於資源耗盡。

瀏覽器格式化全部傳出請求,以強制執行一致且格式良好的協議語義,以保護服務器。相似地,響應解碼是自動完成的,以保護用戶免受惡意服務器的攻擊。

TLS 協議

傳輸層安全性協議 (Transport Layer Security, TLS)是一種經過計算機網絡提供通訊安全性的加密協議。它在許多應用程序中獲得了普遍的應用,其中之一就是 Web 瀏覽器。網站可使用 TLS 保護服務器和Web 瀏覽器之間的全部通訊。該協議由兩層組成: TLS 記錄協議(TLS Record)和 TLS 握手協議(TLS Handshake)。較低的層爲 TLS 記錄協議,位於某個可靠的傳輸協議(例如 TCP)上面。

整個TLS握手包括如下步驟:

  1. 客戶端向服務器發送 「Client hello」 消息,與之一同發送的還有客戶端產生的隨機值和支持的密碼套件。
  2. 服務器經過向客戶端發送 「Server hello」 消息及服務器產生的隨機值進行響應。
  3. 服務器將其證書發送給客戶端,並能夠從客戶端請求相似的證書。 服務器發送 「Server hello done」 消息。
  4. 若是服務器向客戶機請求了證書,客戶機將發送證書。
  5. 客戶端建立一個隨機的 Pre-Master Secret,並使用服務器證書中的公鑰對其進行加密,將加密的 Pre-Master Secret 發送到服務器。
  6. 服務器接收 Pre-Master Secret。 服務器和客戶端均基於預主密鑰生成主密鑰和會話密鑰。
  7. 客戶端向服務器發送 「Change cipher spec」 通知,以指示客戶端將開始使用新的會話密鑰進行散列和加密消息。 客戶端還發送 「Server finished」 消息。
  8. 服務器接收 「Change cipher spec」,並使用會話密鑰將其記錄層安全狀態切換爲對稱加密。 服務器向客戶端發送 「Server finished」 消息。
  9. 客戶端和服務器如今能夠經過他們已創建的安全通道交換應用程序數據。 從客戶端發送到服務器並返回的全部消息都使用會話密鑰加密。

若是任何驗證失敗,則警告用戶 - 例如,服務器正在使用自簽名證書。

同源策略(same-origin policy)

同源是指文檔的來源相同,主要包括三個方面

  • 協議
  • 主機
  • 載入文檔的 URL 端口

如下是一些可能嵌入跨源資源的一些例子:

  • 帶有 <script src =「...」> </ script> 的 JavaScript。 語法錯誤的錯誤消息僅適用於同源腳本
  • 帶有 <link rel =「stylesheet」href =「...」> 的CSS。 因爲 CSS 的寬鬆語法規則,跨源 CSS 須要正確的 Content-Type 標頭。不一樣瀏覽器可能有不一樣的限制
  • 經過 <img> 加載圖片
  • 帶有 <video><audio> 的媒體文件
  • 帶有 <object><embed><applet> 的插件
  • @font-face 的字體。 某些瀏覽器容許跨源字體,其餘瀏覽器須要同源字體
  • 任何有 <frame><iframe> 的東西。 站點可使用 X-Frame-Options 頭部標識來阻止這種形式的跨源交互

以上列表並不是完整,其目的是強調工做中 「最小特權」 的原則。 瀏覽器僅公開應用程序代碼所需的 API 和資源:應用程序提供數據和 URL,瀏覽器格式化請求並處理每一個鏈接的整個生命週期。

值得注意的是,「同源策略」並非一個單一律念。相反,有一組相關的機制來限制對 DOM 訪問、cookie 和會話狀態管理、網絡和瀏覽器的其餘組件。

資源和客戶端狀態緩存

最佳請求是沒有從新請求。在發送請求以前,瀏覽器會自動檢查其資源緩存,執行必要的驗證檢查,並在知足指定條件的狀況下返回資源的本地副本。若是緩存中沒有可用的本地資源,則發出網絡請求,並自動將響應放置在緩存中,以便在有權限的狀況下進行後續訪問。

  • 瀏覽器自動評估每一個資源上的緩存指令
  • 瀏覽器會盡量自動從新驗證過時資源
  • 瀏覽器自動管理緩存大小和資源回收

管理高效且優化的資源緩存很難。 值得慶幸的是,瀏覽器幫咱們處理整個復瑣事情,咱們須要作的就是確保咱們的服務器返回適當的緩存指令; 要了解更多信息,請參閱 客戶端的緩存資源(Cache Resources on the Client)。 這個須要咱們爲頁面上的全部資源提供了 Cache-ControlETag Last-Modified 響應頭部標誌。

最後,瀏覽器的一個常常被忽視的關鍵功能是提供身份驗證、會話和 cookie 管理。瀏覽器爲每一個源維護獨立的 「cookie jars」,提供必要的應用程序和服務器 Api 來讀寫新的 cookie、會話和身份驗證數據,並自動附加上和處理相應的 HTTP 頭以代替咱們自動執行整個過程。

來個例子:

用一個簡單但有說明性的例子來講明將會話狀態管理推放到瀏覽器端的便利之處:同一個通過身份驗證的會話能夠在多個選項卡或瀏覽器窗口之間共享,反之亦然;單個選項卡中的註銷操做將使全部其餘打開的窗口中打開的會話失效。

應用程序 Api 和協議

研究完了網絡服務,終於到達了應用程序 API 和協議這一步。正如咱們所看到的,底層提供了大量關鍵服務:套接字和鏈接管理、請求和響應處理、各類安全策略的執行、緩存等等。每當咱們啓動 HTTP 或 XMLHttpRequest 、長期的 Server-Sent Events 或 WebSocket 會話,或打開 WebRTC 鏈接時,咱們都在與這些底層服務進行交互。

沒有單一的最佳協議或 API。 每一個稍微複雜的應用程序都須要根據各類要求混合使用不一樣的傳輸:與瀏覽器緩存的交互,協議開銷,消息延遲,可靠性,數據傳輸類型等。 某些協議可能提供低延遲傳送(例如,Server-Sent Events,WebSocket),但可能不符合其餘關鍵標準,例如在全部狀況下利用瀏覽器緩存或支持有效二進制傳輸的能力。

如下是一些來提升 Web 應用程序的性能和安全性技巧

  • 始終在請求中使用 「Connection: Keep-Alive」 頭部標識,瀏覽器默認執行此操做,確保服務器使用相同的機制。
  • 使用正確的 Cache-Control,Etag 和 Last-Modified 頭部標識,這樣就能夠節省瀏覽器的下載時間。
  • 花時間調整和優化你的 Web 服務器,這是纔是真正的最有效的地方! 請記住,該過程要針對每一個 Web 應用程序以及你要傳輸的數據的類型要更加具體考慮和處理。
  • 始終使用TLS,特別是若是你的應用程序中有任何類型的身份驗證。
  • 研究瀏覽器在你的應用程序中提供和實施的安全策略。


原文:

https://blog.sessionstack.com...

個人博客即將同步至騰訊雲+社區,邀請你們一同入駐:https://cloud.tencent.com/dev...

你的點贊是我持續分享好東西的動力,歡迎點贊!

交流

乾貨系列文章彙總以下,以爲不錯點個Star,歡迎 加羣 互相學習。

https://github.com/qq44924588...

我是小智,公衆號「大遷世界」做者,對前端技術保持學習愛好者。我會常常分享本身所學所看的乾貨,在進階的路上,共勉!

關注公衆號,後臺回覆福利,便可看到福利,你懂的。

clipboard.png

相關文章
相關標籤/搜索