摘要:但對于很多資源,則可以利用協議減少網絡負載。采用的是多進程的資源加載機制。目前大多數瀏覽器都有磁盤緩存機制,因為緩存機制確實能夠提高網頁的加載速度。
微信公眾號:愛寫bugger的阿拉斯加前言
如有問題或建議,請后臺留言,我會盡力解決你的問題。
此文章是我最近在看的【WebKit 技術內幕】一書的一些理解和做的筆記。
而【WebKit 技術內幕】是基于 WebKit 的 Chromium 項目的講解。
書接上文 瀏覽器內核之WebKit 架構與模塊
網絡和資源加載是網頁的加載和渲染過程中的第一步,加載的資源包括以下內容:
在資源類的前面加上 “Cached” 字樣,是因為效率問題而引入的緩存機制,所有對資源的請求都會先獲取緩存中的信息, 以決定是否向服務器提出資源請求。
2. 資源緩存資源的緩存機制是提高資源使用效率的有效方法。
它的基本思想是建立一個資源的緩存池。
當 WebKit 需要請求資源的時候,先從資源池中查找是否存在相應的資源。如果有,WebKit 則取出以便使用;如果沒有,WebKit 創建一個新的 CachedResource 子類的對象,并發送真正的請求給服務器,WebKit 收到資源后將其設置到該資源類的對象中去,以便于緩存后下次使用。這里緩存指的是內存緩存,而不同于后面在網絡棧部份的磁盤緩存。
WebKit 從資源池中查找資源的關鍵字是 URL, 因為標記資源唯一的特征就是資源的 URL 。這也意味著,假如兩個資源有不同的 URL ,但是它們的內容完全一樣,也被認為是兩個不同的資源。其實,上面是個簡單的示意圖,真實的過程比這里要復雜,這其中涉及到了資源的生命周期和失效機制。
3. 資源加載器按照加載器的類型來分,WebKit 總共有三種類型的加載器。
由于從網絡獲取資源是一個非常耗時的過程,通常一些資源的加載是異步執行的,也就是說網絡資源的獲取和加載不會阻礙當前 WebKit 的渲染過程,例如圖片、CSS 文件。
當然,網頁也存在某些特別的資源會阻礙主線程的渲染過程,例如 Javascript 代碼文件。這會嚴重影響 WebKit 下載資源的效率。因為主線程被阻礙了,后面的解析工作沒有辦法繼續往下進行,所以對于 HTML 網頁中后面使用的資源也沒有辦法知道并發送下載請求。
這時候,WebKit 會這樣:當前的主線程被阻礙時,WebKit 會啟動另外一個線程去遍歷后面的 HTMl 網頁,收集需要的資源 URL,然后發送請求,這樣就可以避免被阻礙。與此同時,WebKit 能夠并發下載這些資源,甚至并發下載 JavaScript 代碼資源。這種機制對于網頁的加載提速很是明顯。
4. 資源的生命周期資源池中的生命周期是什么呢?資源池不能無限大,必須要用相應的機制來替換其中的資源,從而加入新的資源。資源池使用的機制其實很簡單,就是采用 LRU(Lease Recent Used 最近最少使用)算法。
另外一方面,當一個資源加載后,通常它會被放入資源池,以便之后使用。問題是,WebKit 如何判斷下次使用的時候是否需要更新該資源從而對服務器重新請求?因為服務器可能在某段時間之后更新了該資源。
考慮這樣的場景,當用戶打開網頁后,他想刷新當前的頁面。這種情況下,資源池會出現怎樣的情況呢?是清除所有的資源,重新獲得?還是直接利用當前的資源?都不是。對于某些資源,WebKit 需要直接重新發送請求,要求服務器將內容重新發送過來。但對于很多資源,WebKit 則可以利用 HTTP 協議減少網絡負載。在 HTTP 協議的規范中對此有規定,瀏覽器可以發送消息確認是否需要更新,如果有,瀏覽器則重新獲取該資源;否則就需要利用該資源。
WebKit 的做法是,首先判斷資源是否在資源池中,如果是,那么發送一個 HTTP 請求給服務器,說明該資源在本地的一些信息,例如該資源什么時間修改的,服務器則根據該信息作判斷,如果沒有更新,服務器則發送回狀態碼 304 ,表明無需更新,那么直接利用資源池中原來的資源;否則。WebKit 申請下載最新的資源內容。
5. Chromium 多進程資源加載資源的實際加載在各個 WebKit 移植中有不同的實現。Chromium 采用的是多進程的資源加載機制。
圖4-11 描述了關于 Chromium 如何利用多進程架構來完成資源的加載,主要是多個 Render 進程和 Browser 進程之間的調用棧涉及的主要類。
Render 進程在網頁的加載過程中需要獲取資源,但是由于安全性(實際上,當沙箱模型打開的時候,Render 進程是沒有權限去獲取資源的)和效率上(資源共享等問題)的考慮,Render 進程的資源獲取實際上是通過進程間通信將任務交給 Browser 進程來完成,Browser 進程有權限從網絡或者本地獲取資源。
在 Chromium 架構的 Renderer 進程中,ResourceHandleInternal 類通過 IPCResource-LoaderBridge 類同 Browser 進程通信。IPCResourceLoaderBridge 類繼承自 ResourceLoaderBridge 類,其作用是負責發起請求的對象和回復結果的解釋工作,實際消息的接收和派發交給 ResourceDispatcher 類來處理。
資源統一交由 Browser 進程來處理,這使得資源在不同網頁間的共享變得很容易。因為每個 Renderer 進程某段時間內可能有多個請求,同時還有多個 Renderer 進程,Browser 進程需要處理大量的資源請求,這就需要一個處理這些請求的調度器,這就是 Chromium 中的 ResourceScheduler。
6. 網絡棧 6.1 WebKit 的網絡棧上圖4-13 是 “ent” 所包括的主要子目錄,也是 Chromium 網絡棧的主要模塊。這里面除了一些基礎的部分,例如 HTTP 協議。NDS 解析等模塊,還包含了 Chromium 為了減少網絡時間 而引入的新技術,例如 SPDY 、QUIC 等
圖4-14 描述了從 URLRequest 類到 Socket 類之間的調用過程。以 HTTP 協議為例,圖中列出了建立 TCP 的 socket 連接過程中涉及的類。
首先是 URLRequest 類被上層調用并啟動請求的時候,它會根據 URL 的 “scheme” 來決定需要創建什么類型的請求。“scheme” 也就是 URL 的協議類型,例如 “http://”、“file://” ,也可以是自定義的 scheme ,例如 Android 系統的 “file://android_asset/”。 URLRequest 對創建的是一個 URLRequestJob 子類的一個對象,例如圖中的 URLRequestJob 類。為了支持自定義的 scheme 處理方式, Chromium 使用工廠模式。
URLRequestJob 類和它的工廠類 URLRequestJobFactory 的管理工作都由 URLRequestJobManager 類負責。基本思路是,用戶可以在該類中注冊多個工廠,當有 URLRquest 請求時,先由工廠檢查它是否需要處理該 “scheme” ,如果沒有,工廠管理類繼續交給下一個工廠類來處理。最后,如果沒有任何工廠能夠處理,Chromium 則交給內置的工廠來檢查和處理是否為 “http://”、“ftp://”、或者 “file://” 等。
圖 4-15 就是描述這些類的關系。
當用戶設置代理時,用戶代理依賴以下類來處理。
圖 4-17 不僅描述上面這些類,同時也描述了 Chromium 中獲取網絡代理的過程。圖中數據表示獲取網絡代理的次序,其中的分支 3.1 和 4.1 分別表示簡單的代理設置和代理腳本設置的處理過程。
8. 磁盤本地緩存如果沒有磁盤緩存,當用戶訪問網頁的時候,每次瀏覽器都要需要從網站下載網頁,圖片、js 等資源,這其實費力又不討好。解決這一問題的方法就是將之前瀏覽器下載的資源保存下來,存到磁盤中,以備今后使用。當然,資源是有時效性的,也會變得不再有效,所以需要有相應的退出機制來解決這一問題。目前大多數瀏覽器都有磁盤緩存機制,因為緩存機制確實能夠提高網頁的加載速度。
8.1 特性為了適應網絡資源的本地緩存需求, Chromium 的本地磁盤緩存有幾個特性或者要求。
雖然需要緩存的資源可能很多,但磁盤空間不是無限大的,所以必須要有相應的機制來移除合適的緩存資源,以便加入新的資源。
能夠確保在瀏覽器崩潰時不破壞文件,至少能夠保護原先在磁盤中的數據。
能夠高效和快速地訪問磁盤中現有的數據結構,支持同步和異步兩種訪問方式。
能夠避免同時存儲兩個相同的資源。
能夠很方便地從磁盤中刪除一個項,同時可以在操作一個項的時候不受其他請求的影響。
磁盤不支持多線程訪問,所以需要把所有磁盤緩存的操作放入多帶帶的一個線程。
升級版本時,如果磁盤緩存的內部存儲結構發生改變, Chromium 仍然能夠支持老版本的結構。
8.2 結構內部結構主要有個類:Backend 和 Entry 。 Backend 類表示整個磁盤緩存,是所有針對磁盤緩存操作的主入口,表示的是一個緩存表。Entry 類指的是表中的表項。緩存通常是一個表,對于整個表的操作作用在 Backend 類中,包括創建表中的一個個項,每個項由關鍵字來唯一確定,這個關鍵字注是資源的 URL。而對項目內的操作包括讀寫等都是由 Entry 類來處理。
9. Cookie 機制Cookie 格式就是一系列的 “關鍵字+值” 對,一個簡單的例子如下:
test1 = webkit; test2 = chromium, Expires = Sun, 30 Oct 2016 21:35:00 GMT; Domain = .myweb.cm;
例子中包括兩個自定義的關鍵字,分別是 “test1” 和 “test2” ,它們的值分別為 “webkit” 和 “chromium” 。后面的則是預定義的關鍵字 “Expires” 和 “Domain”,表示的是該 Cookie 的失敗時間和該 Cookie 對應的域。基于安全性考慮,一個網頁的 Cookie 只能被該網頁(或者說是該域的網頁)訪問。
根據 Cookie 的時效性可以將 Cookie 分成兩種類型,第一種是會話型 Cookie (Session Cookie)。如果 Cookie 沒有設置失效時間,就是會話型 Cookie。第二種是持續型 Cookie (Persistent Cookie),也就是當瀏覽器退出的時候,仍然保留 Cookie 的內容。該類型的 Cookie 有一個有效期,在有效期內,每次訪問該 Cookie 所屬域的時候,都需要將該 Cookie 發送給服務器,這樣服務器能夠有效追蹤用戶的行為。
Chromium 中支持 Cookie 的機制也較為簡單和清晰,如圖 4-23 所示的是 Chromium 所設計和使用的主要類及其關系。CookieMonster 是Cookie 機制中最重要的類,實際上相當于 Cookie 管理器。
它包括幾個作用:
第一是實現 CookieStore 的接口,它是對外的接口,調用者可以設置和獲得 Cookie;
第二是報告各種 Cookie 的事件,例如更新信息等,主要使用 Delegate 類,
第三是 Cookie 對象的集合,也就是 CanonicalCookie 的集合,每個 CanonicalCookie 對象都是保存在內存中的,當需要存儲到磁盤的時候使用 PersistentCookieStore 類,具體由 SQLitePersistentCookieStore 類負責實際的存儲動作。
10. 安全機制HTTP 是一種使用明文來傳輸數據的應用層協議。構建在 SSL 之上的 HTTPS 提供了安全的網絡傳輸機制,現已被廣泛應用于網絡上。典型的是電子商務、銀行支付方面的應用。基本上所有的瀏覽器都支持該協議, Chromium 當然也不例外。
不僅如此,Chromium 也支持一種新的標準,這就是 HSTS (HTTP Strict TransportSecurity)。該協議能夠讓網絡服務器聲明它只支持 HTTPS 協議,所以瀏覽器能夠理解服務器的聲明,發送基于 HTTPS 的連接和請求。通常情況下,瀏覽器用戶不會輸入 “scheme(http://)”,瀏覽器的補充功能通常會加入該 “scheme” ,但是,服務器可能需要輸入 ”https://“ 。在這樣子的情況下,該協議就顯得非常有用。一般情況下,服務在返回的消息頭中加入以下信息表明它支持該標準:
Strict-Transport-Security: max-age=16070400; includeSubDomains
11. 高性能網絡棧Chromium 的網絡模塊有兩個重要目標,其一是安全,其二是速度。為此,該項目引入了很多 WebKit 所沒有的新技術。
11.1 DNS 預取和 TCP 預連接(Preconnect)一次 DNS 查詢的平均時間大概是 60 ~ 120ms 之間或者更長,而 TCP 的三次握手時間大概也是幾十毫秒或者更長。為了有效減少這段時間,Chromium 引入了 DNS 預取和 TCP 預連接,它們都是由 Chromium 的 ”Predictor“ 機制來實現的。
首先是 DNS 預取技術。它的主要思想是利用現有的 DNS 機制,提前解析網頁中可能的網絡連接。具體來講,當用戶正在瀏覽當前網頁的時候,Chromium 提取網頁中的超鏈接,將域名抽取出來,利用比較少的 CPU 和網絡帶寬來解析這些域名或者 IP 地址,這樣一來,用戶根本感覺不到這一過程。當用戶單擊這些鏈接的時候,可以節省不少時間,特別在域名解析比較慢的時候,效果特別明顯。
DNS 預取技術是利用系統的域名解析機制,好處是它不會阻礙當前網絡棧的工作。DNS 預取技術針對多個域名采取并行處理的方式,每個域名的解析須由新開啟的一個線程來處理,結束后此線程即退出。
當然, DNS 預取技術不僅應用于網頁中的超鏈接,當用戶在地址欄中輸入地址后,候選項同輸入的地址很匹配的時候,在用戶敲下回車鍵獲取網頁之前, Chromium 已經開始使用 DNS 預取技術解析該域名了。
Chromium 使用追蹤技術來獲取用戶從什么網頁跳轉到另外一個網頁。可以利用這些數據,一些啟發式規則和其他一些暗示來預測用戶下面會單擊什么超鏈接,當有足夠的把握時,它便先 DNS 預取,更進一步,還可以預先建立 TCP 連接。聽起來夠智能的吧,是的。但是這對用戶的隱私是一個極大的挑戰,它甚至能預測你單擊什么超鏈接。
同 DSN 預取技術一樣,追蹤技術不會應用于網頁中的超鏈接,當用戶在地址欄中輸入地址,如候選項同輸入的地址很匹配,則在用戶敲擊下回車鍵獲取該網頁之前,Chromium 就已經開始嘗試建立 TCP 連接了。
11.2 HTTP 管線化(PipeLining)很多時候,服務器和瀏覽器通話是按順序來的,瀏覽器發送一個請求給服務器,等到服務器的回復后,才會發送另外一個請求。弊端是效率極差。
HTTP 1.1 開始增加了管線化技術,Chromium 也支持這一技術,但它需要服務器的支持兩者配合才能實現 HTTP 管線化。HTTP 管線化技術是一項同時將多個 HTTP 請求一次性提交給服務器的技術,因此無需等待服務器的回復,因為它可能將多個 HTTP 請求填充在一個 TCP 數據包內。HTTP 管線化需要在網絡上傳輸較少的 TCP 數據包,因此減少了網絡負載。
圖4-24 描述了 HTTP 管線化技術是如何傳送請求和回復的。
請求結果的管線化使得 HTML 網頁加載時間動態提升,特別是在具體有高延遲的連接環境下。在速度較快的網絡連接環境下,提速可能不是很明顯。因為這些請求還是有明顯的先后順序的。管線化機制需要通過永久連接(Persistent Connection)完成,并且只有 GET 和 HEAD 等請求可以進行管線化,使用場景有很大的限制。
11.3 SPDYSPDY 就是為了解決網絡延遲和安全性問題。根據 Google 的官方數據,使用 SPDY 協議的服務器和客戶端可以將網絡加載的時間減少 64。
SPDY 協議是一種新的會話層協議,因為網絡協議 是一種棧式結構,它被定義在 HTTP 協議和 TCP 協議之間,SPDY 協議的核心思想 是多數復用,僅使用一個連接來傳輸一個網頁中的眾多資源。
11.4 QUICQUIC 是一種新的網絡傳輸協議,主要目標是改進 UDP 數據協議的能力。同 SPDY 建立在傳輸層之上不同,QUIC 所要解決的問題就是傳輸層的傳輸效率,并提供了數據的加密,所以,SPDY 可以在 QUIC 之上工作。
12. 實踐:高效的資源使用策略WebKit 和 Chromium 為了高效率地下載資源,設計出了各種各樣的策略和新技術。
## 12.1 DNS 和 TCP 連接
DNS 和 TCP 連接占用大量的時間,所以為了高效地加載網頁,網頁開發者可以從以下方面著手改變以減少這一部分的時間。
減少鏈接的重定向。有些網頁中使用了大量重定向,可能還會有很多次重定向,還不僅要求瀏覽器建立多次鏈接,同時還需要多次 DNS 解析,這會阻礙 DNS 預取技術的應用,應該盡量避免。
利用DNS預取機制。網頁的開發者當然知道需要鏈接的 URL,為了讓瀏覽器也知道這些鏈接,開發者可以指定需要預取的 URL。
搭建支持 SPDY 協議的服務器,當然指的是那些需要使用 HTTPS 協議的網站。
避免錯誤的鏈接請求。有些網頁中包含了一些失效的鏈接,當瀏覽器試圖獲取該鏈接對應的資源的時候,就會占用網絡資源。
12.2 資源的數量我們也可以通過減少網頁中所需的資源數量來改善網頁的加載:
在 HTML 網頁中內嵌小型的資源,也就是當資源比較小的時候,可以將它們直接放在網頁中,可能的資源如 CSS、JavaScript 和圖片等。比如圖片 用 webpack 直接打包成 base64。
合并一些資源,例如 CSS、JavaScript 和圖片。常見的就是一些網頁中大量使用的小圖片,可以將它們合并成一張大的圖片以供使用。
12.3 資源的數據量對于每個資源而言,通過減少它的數據量來提高網頁的加載速度:
使用瀏覽器本地磁盤緩存機制。因為 HTTP 協議支持資源的失效機制,通過對資源設置適當的失效期來減少瀏覽器對資源的重復獲取。
啟用資源的壓縮技術。例如,對于圖片而言,可以使用 zip 壓縮技術,然后在 HTTP 消息頭中說明該資源經過壓縮,這樣可以有效減少網絡傳輸的數據量。
最后希望本文對你有點幫助。
下期分享 第五章 HTML解釋器與模型 敬請期待。
對 全棧開發 有興趣的朋友可以掃下方二維碼關注我的公眾號 —— 愛寫bugger的阿拉斯加
分享 web 開發相關的技術文章,熱點資源,全棧程序員的成長之路。
大家一起交流成長。
只要關注公眾號并回復 福利 便送你六套、并且每套價值 3999 元的視頻資源: Python、Java、Linux、Go、vue、react、javaScript
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/97206.html
摘要:書接上文瀏覽器內核之資源加載與網絡棧本文介紹的模型之后,深入的核心部分,剖析的解釋器是如何將從網絡或者本地文件獲取的字節流轉成內部表示的結構樹。事件處理最重要就是事件捕獲和事件冒泡這兩種機制。 showImg(https://segmentfault.com/img/remote/1460000016215814); 微信公眾號:愛寫bugger的阿拉斯加如有問題或建議,請后臺留言,我...
摘要:響應由三個部分組成,分別是狀態行消息報頭響應正文。詳情參考小汪之前寫的文章瀏覽器內核之解釋器和模型解釋解釋過程是指從字符串經過解釋器處理后變成渲染引擎內部規則的表示過程。 showImg(https://segmentfault.com/img/remote/1460000016404846); 前言 小汪最近在看【WebKit 技術內幕】一書,說實話,這本書寫的太官方了,不通俗易懂。...
摘要:多線程的主要目的就是為了保持用戶界面的高響應度,保證線程進程中的主線程不會被任何其他費用時的操作阻礙從而影響了對用戶操作的響應。 showImg(https://segmentfault.com/img/remote/1460000016113034); 微信公眾號:愛寫bugger的阿拉斯加如有問題或建議,請后臺留言,我會盡力解決你的問題。 前言 此文章是我最近在看的【WebKit ...
摘要:如果看完本文后,還對進程線程傻傻分不清,不清楚瀏覽器多進程瀏覽器內核多線程單線程運行機制的區別。因此準備梳理這塊知識點,結合已有的認知,基于網上的大量參考資料,從瀏覽器多進程到單線程,將引擎的運行機制系統的梳理一遍。 前言 見解有限,如有描述不當之處,請幫忙及時指出,如有錯誤,會及時修正。 ----------超長文+多圖預警,需要花費不少時間。---------- 如果看完本文后,還...
閱讀 1875·2021-09-27 13:35
閱讀 3429·2019-08-30 14:16
閱讀 2483·2019-08-30 10:52
閱讀 859·2019-08-29 16:35
閱讀 1416·2019-08-29 15:22
閱讀 3641·2019-08-23 18:21
閱讀 3132·2019-08-23 18:00
閱讀 3123·2019-08-23 16:50