JavaScript 工做原理之十二-網絡層探祕及如何提升其性能和安全性

原文請查閱這裏,略有改動,本文采用知識共享署名 4.0 國際許可協議共享,BY Trolandjavascript

本系列持續更新中,Github 地址請查閱這裏java

這是 JavaScript 工做原理的第十二章。git

正如在以前關於渲染引擎的文章中所講的那樣,咱們相信好的和偉大的 JavaScript 開發者之間的差異在於後者不只僅只是理解了語言的具體細節還了解其內部構造和運行環境。github

網絡簡史

49 年前,ARPAnet 誕生了。它是早期的報文分組交換網絡及第一個實現 TCP/IP 協議套件的網絡。該網絡連通了加利福亞大堂和斯坦福研究所。20 年後,Tim Berners-Lee 分發了一個後來爲人所熟知的萬維網的 『Mesh』草案。在 49 年的時間裏,網絡走過了一段漫長的旅程,從僅僅只是是兩臺電腦間交換數據報文到至少 7500 萬臺服務器,38 億人使用互聯網以及 13 億個網站。chrome

本文將試着分析現代瀏覽器使用哪些技術來自動提高應用性能(有些你甚至不瞭解),而後着重介紹瀏覽器網絡層。最後,提供一些讓瀏覽器提高網絡應用程序性能的技巧。跨域

概述

現代瀏覽器專門爲快速,高效和安全數據傳輸的網絡應用/網站而設計開發的。擁有數以百計的組件運行於各個不一樣的層級,從進程管理和安全沙箱到 GPU 管線,音頻和視頻及其它更多等等,網絡瀏覽器更相似於一個操做系統而不只僅只是一個軟件。瀏覽器

瀏覽器的總體性能是由一些大型的組件所決定的,這些組件包括:解析,佈局,樣式計算,JavaScript 和 WebAssembly 執行,渲染,固然還有網絡堆棧。緩存

通常狀況下,工程師們會把網絡堆棧當作是一個性能瓶頸。常常會發生這樣的狀況由於從網絡抓取全部的資源會堵塞渲染剩下的步驟。爲了更加高效的網絡層,它須要不只僅只是扮演套接字管理員的角色。在咱們看來獲取資源是一個很是簡單的機制,可是實際上它集成自身的優化準則,接口和服務的一整套平臺。安全

網頁開發者不須要擔憂單獨的 TCP 或者 UDP 數據包,請求格式化,緩存以及其它正在發生的一切。瀏覽器會處理這些複雜的玩意,這樣就能夠專一開發本身的程序。可是,知道其內部的原理能夠幫助開發者開發出更加高效和安全的程序。服務器

本質上,當用戶開始和瀏覽器發生交互所產生的狀況以下:

  • 用戶在瀏覽器地址欄中輸入 URL 地址。
  • 在網絡上查找指定 URL 的資源,瀏覽器開始檢查本地和應用程序緩存並試着使用本地副原本響應資源的請求。
  • 當緩存不可用,瀏覽器使用 URL 中的域名而後根據域名從 DNS 處獲取服務器的 IP 地址。若是有域名緩存,將不須要進行 DNS 查詢。
  • 瀏覽器建立一個 HTTP 報文代表其請求遠程服務器的某個網頁。
  • 報文被傳輸到 TCP 層,該層會在 HTTP 報文頭部添加其自身的信息。該信息是保持建立的會話的必要信息。
  • 而後在 IP 層處理報文,該層的主要職責即找出從用戶發送報文到遠程服務器的路徑。在 HTTP 報文頭部添加該路徑信息。
  • 傳輸報文到遠程服務器。
  • 一旦接收到報文,以相似的方式返回響應數據。

W3C Navigation Timing specification 提供了瀏覽器接口及瀏覽器中每一個請求背後的可視化計時和性能數據。讓咱們瀏覽下這些組件,由於每一個組件在獲取最佳用戶體驗方面扮演了重要的角色。

整個網絡請求過程是至關複雜的而且有許多的層次結構,每一層都有可能成爲性能瓶頸。這就是爲何瀏覽器使用各類技術努力提高其性能,以便把整個網絡通訊的性能損耗降至最低。

套接字管理

看些新技術吧:

  • -由應用程序協議,域名和端口號的三個部分組成(好比 https, www.example.com, 443)
  • 套接字池-屬於同源的一組套接字(全部的主流瀏覽器都限制套接字池最多隻能有 6 個套接字)

JavaScript 和 WebAssembly 禁止開發者操做單獨的網絡套接字的生命週期,這樣是至關的明智的。這樣不只僅可讓你頭髮少掉點並且可讓瀏覽器自動優化大量的性能,這些性能包括套接字重用,請求優化和延遲綁定,協議協商,強制鏈接限制及其它的優化措施。

實際上,現代瀏覽器更一步地將請求管理週期從套接字管理中剝離了出來。用套接字池來組織套接字,以源來分組套接字,每一個套接字池強制限制其鏈接數和安全約束。排隊,優先化等待的請求,而後和套接字池中的單個套接字綁定。若是不是服務器主動關閉這些鏈接,多個請求能夠自動重用相同的套接字。

因爲建立一個新的 TCP 鏈接會帶來額外的性能開銷,重用鏈接會爲其自帶來極大的性能提高。默認狀況下,當發起請求的時候,瀏覽器使用所謂的 『keepalive』機制以節省建立到服務器的新鏈接所耗費的時間。建立一個新的 TCP 鏈接的平均時間爲:

  • 本地請求-23 毫秒
  • Transcontinental 請求-120 毫秒
  • Intercontinental 請求-225 毫秒

這樣的架構衍生出了一些其它的優化方法。請求能夠依據優先級來以不一樣的順序執行。瀏覽器能夠優化全部套接字間的帶寬分配或者它能夠建立套接字以等待預期的請求。

如上所述,這些都是瀏覽器所控制而不用開發者進行干預。但這並不意味着咱們無所事事了。選擇正確的數據傳輸所用的網絡通訊模式,類型和頻率,正確的協議類型以及正確的服務器堆棧隧道/優化對於提高整個程序的性能有着相當重要的做用。

一些瀏覽器甚至更進一步。例如,當你使用 Chrome 的時候,當用戶使用的時候它會進行自我學習從而變得更加快速。它基於訪問過的網頁和典型的瀏覽器模式來進行學習,這樣就能夠預期可能的用戶行爲且在用戶進行任意操做以前進行操做。最簡單的例子即當用戶懸停在某個連接上的時候預渲染頁面。若是你想學習更多關於 Chrome 優化技術的文章,能夠查看 High-Performance Browser Networking 這本書的 www.igvita.com/posa/high-p… 章節。

網絡安全和沙箱

容許瀏覽器操做單獨的套接字有另外一個很是重要的目的即:瀏覽器就能夠針對不被信任的程序資源強制實施一套一致的安全和政策約束措施。例如,瀏覽器禁止經過 API 直接訪問原始網絡套接字,由於這樣會致使任意可疑的程序隨意鏈接任意主機。瀏覽器也強制鏈接數限制以保護服務器免受因爲客戶端訪問而耗盡其資源。

瀏覽器格式化全部流出的請求以強制格式正確和一致的協議語義來保護服務器。一樣地,瀏覽器會自動解碼響應內容以保護用戶免受可疑服務器的攻擊。

TSL 協商

Transport Layer Security (TLS) 是一個爲計算機網絡提供通訊安全的加密協議。它普遍應用於大量應用程序,其中之一即瀏覽網頁。網站可使用 TLS 來保證服務器和網頁瀏覽器之間的全部通訊安全。

整個 TLS 握手過程包含如下幾個步驟:

  1. 客戶端向服務器發送 『Client hello』 信息,附帶着客戶端隨機值和支持的密碼組合。
  2. 服務器返回給客戶端 『Server hello』信息,附帶着服務器隨機值。
  3. 服務器返回給客戶端認證證書及或許要求客戶端返回一個相似的證書。服務器返回『Server hello done』信息。
  4. 若是服務器要求客戶端發送一個證書,客戶端進行發送。
  5. 客戶端建立一個隨機的 Pre-Master 密鑰而後使用服務器證書中的公鑰來進行加密,向服務器發送加密過的 Pre-Master 密鑰。
  6. 服務器收到 Pre-Master 密鑰。服務器和客戶端各自生成基於 Pre-Master 密鑰的主密鑰和會話密鑰。
  7. 客戶端給服務器發送一個 『Change cipher spec』的通知,代表客戶端將會開始使用新的會話密鑰來哈希和加密消息。客戶端也發送了一個 『Client finished』的消息。
  8. 服務器接收到『Change cipher spec』的通知而後使用會話鑰匙來切換其記錄層安全狀態爲對稱加密狀態。服務器返回客戶端一個 『Server finished』消息。
  9. 客戶端和服務器如今能夠經過創建的安全通道來交換程序數據。全部客戶端和服務器之間發送的信息都會使用會話密鑰進行加密。

每當發生任何驗證失敗的時候,用戶會收到警告。好比服務器使用自簽名的證書。

同源策略

當兩個頁面的協議,端口(若是有指定)以及主機名都是同樣的則稱爲同源。

如下爲一些可能包含跨域的資源示例:

  • <script src=」…」></script> 裏面的 JavaScript 代碼。語法錯誤的錯誤信息僅適用於同源腳本。
  • <link rel=」stylesheet」 href=」…」> 的 CSS。因爲 CSS 的鬆散語法規則,跨域 CSS 要求正確的 Content-Type 頭。各個瀏覽器的限制不一樣。
  • <img> 圖片
  • <video><audio> 媒體文件
  • <object><embed> 和 <applet> 插件
  • @font-face 字體。一些瀏覽器容許跨域字體,其它則要求同源字體。
  • 和 相關的一切內容。網站可使用  X-Frame-Options 頭來防止此種跨域交互。

    以上的列表還遠遠不夠;該列表旨在強調工做中的『最小特權』原則。瀏覽器只爲應用程序代碼暴露出其所必需的接口和資源:應用提供數據和 URL 地址,而後瀏覽器格式化請求及處理每條鏈接的整個生命週期。

    須要注意的是並無一個簡單的 『同源策略』概念。

    相反,有一系列相關的機制來強制限制瀏覽器的 DOM 訪問,cookie 和 會話狀態管理,網絡鏈接和其它組件。

    資源和客戶端狀態緩存

    最好和最快的請求即不建立請求。在分派一個請求前,瀏覽器自動檢查其資源緩存,進行必要的驗證檢查而後當指匹配指定的條件時返回一份本地資源拷貝。若是緩存中沒有可用的本地資源,則發起網絡請求而後把響應內容自動放置於緩存中以備以後的訪問(若是這是被容許的)。

    • 瀏覽器自動爲每一個資源求值緩存指令。
    • 當條件容許時,瀏覽器自動從新恢復過時資源
    • 瀏覽器自動處理緩存和資源回收的大小

    管理一個高效和優化的資源緩存是很是困難的。謝天謝地,瀏覽器爲咱們處理了整個複雜的玩意,而咱們所須要作的即保證服務器返回恰當的緩存指令;想了解更多能夠看 客戶端資源緩存 文章。你爲網頁上的全部資源添加 Cache-Control,ETag,和 Last-Modified 的響應頭信息。

    最後,一個常常被忽略但相當重要的瀏覽器功能即其提供了驗證,會話和 cookie 管理。瀏覽器爲每一個源維護單獨的『cookie jars』,經過提供必要的程序和服務器接口來讀寫新的 cookie,會話和認證數據,以及自動掛載和處理適當的 HTTP 頭來爲咱們自動處理整個過程。

    例子:

    一個簡單但明瞭的用來展現瀏覽器的延遲會話狀態管理的方便性的例子即:多個選項卡或者瀏覽器窗口能夠共享一個認證會話,反之亦然;一個選項卡中的登出操做可使全部其它打開窗口的會話失效。

    應用程序接口和協議

    瞭解了網絡服務以後,最終要講到應用程序接口和協議。衆所周知,更底層的結構提供了一組普遍的重要服務:套接字和鏈接管理,請求和響應處理,各類安全策略,緩存及其它更多的強制措施。每當初始化一個 HTTP 請求或者 XMLHttpRequest,一個持久的服務推事件或者 WebSocket 會話抑或打開一個 WebRTC 鏈接,咱們就是在和部分或者全部這些底層服務進行交互。

    沒有單一的最佳協議或者接口。每一個複雜的程序都會基於不一樣的要求混合使用不一樣的傳輸協議:和瀏覽器緩存的交互,協議開銷,消息延遲,可靠性,數據傳輸類型以及其它。一些協議擁有低數據傳輸延遲的特性(好比服務器推事件,WebSocket),可是可能不符合其它重要的場合,好比利用瀏覽器緩存或者支持任意狀況下的二進制數據傳輸的能力。

    一些能夠用來提高程序性能和安全的小技巧

    • 一直在請求中使用 『Connection: Keep-Alive』頭信息。瀏覽器默認在請求頭中添加 『Connection: Keep-Alive』。保證服務器也使用一樣的機制。
    • 使用合適的 Cache-Control,Etag 和 Last-Modified 頭信息以便節省瀏覽器的下載時間。
    • 花些時間調整和優化服務器。這是奧祕所在!注意這一過程是否針對每一個程序和所傳輸的數據。
    • 一直使用 TLS!特別是若是程序中包含有任意類型的認證。
    • 研究瀏覽器所提供的安全策略而且在程序中強制實施。

    擴展

    關於字體文件的跨域問題能夠查看這裏這裏

    打個廣告 ^.^

    今日頭條招人啦!發送簡歷到 likun.liyuk@bytedance.com ,便可走快速內推通道,長期有效!國際化PGC部門的JD以下:c.xiumi.us/board/v5/2H…,也可內推其餘部門!

    本系列持續更新中,Github 地址請查閱這裏

相關文章
相關標籤/搜索