標籤:

從URL開始,定位世界

從我們輸入URL並按下回車鍵到看到網頁結果之間發生了什麼?換句話說,一張網頁,要經歷怎樣的過程,才能抵達用戶面前?下面來從一些細節上面嘗試一下探尋裡面的秘密。

前言:鍵盤與硬體中斷

說到輸入URL,當然是從手敲鍵盤開始。對於鍵盤,生活中用到的最常見的鍵盤有兩種:薄膜鍵盤、機械鍵盤。

  • 薄膜鍵盤:由面板、上電路、隔離層、下電路構成。有外觀優美、壽命較長、成本低廉的特點,是最為流行的鍵盤種類。鍵盤中有一整張雙層膠膜,通過膠膜提供按鍵的回彈力,利用薄膜被按下時按鍵處碳心於線路的接觸來控制按鍵觸發。
  • 機械鍵盤:由鍵帽、機械軸組成。鍵盤打擊感較強,常見於遊戲發燒友與打字愛好者。每一個按鍵都有一個獨立的機械觸點開關,利用柱型彈簧提供按鍵的回彈力,用金屬接觸觸點來控制按鍵的觸發。

(機械鍵盤獨立觸點原理圖)

鍵盤傳輸信號到操作系統後便會觸發硬體中斷處理程序硬體中斷是操作系統中提高系統效率、滿足實時需求的非常重要的信號處理機制,它是一個非同步信號,並提供相關中斷的註冊表(IDT)與請求線(IRQ)。鍵盤被按壓時,將通過請求線將信號輸入給操作系統,CPU在當前指令結束之後,根據註冊表與信號響應該中斷並利用段寄存器裝入中斷程序入口地址。具體可參看操作系統與彙編相關書籍。

當然本文主要不是介紹硬體與操作系統中的細節,前言只是想說明,從輸入URL到瀏覽器展現結果頁面之間有太多底層相關的知識,懷著一顆敬畏的心並且在有限的篇幅中是無法詳細闡述的,所以本文會將關注點放在一個稍高的角度上來看,從瀏覽器替我們發送請求之後到看到頁面顯示完成這中間中發生了什麼事情,比如DNS解析、瀏覽器渲染。

下圖是從用戶發送一條請求開始到最終看到頁面的流程簡化圖,本文將以該請求為線索,在下面一步一步探索其中的細節。

瀏覽器解析URL

按下回車鍵之前

比如我按下一個「b」鍵,會出現很多待選URL給我,第一個便是百度。那麼其實是在瀏覽器接收到這個消息之後,會觸發瀏覽器的自動完成機制,會在你之前訪問過的搜索最匹配的相關URL,會根據特定的演算法,甚至涉及到機器學習來分析出你有可能想搜索的關鍵字,顯示出來供用戶選擇。

按下回車鍵之後

依據上述鍵盤觸發原理,一個專用於回車鍵的電流迴路通過不同的方式閉合了。然後觸發硬體中斷,隨之操作系統內核去處理對應中斷。省略其中的過程,最後交給了瀏覽器這樣一個「回車」信號。那麼瀏覽器(本文涉及到的瀏覽器版本為Chrome 61)會進行以下但不僅限於以下炫酷(亂七八糟)的步驟:

  1. 解析URL:您輸入的是http還是https開頭的網路資源 / file開頭的文件資源 / 待搜索的關鍵字?然後瀏覽器進行對應的資源載入進程。
  2. URL轉碼:RFC標準中規定部分字元可以不經過轉碼直接用於URL,但是漢字不在範圍內。所以如果在網址路徑中包含漢字將會被轉碼,比如https://zh.wikipedia.org/wiki/HTTP嚴格傳輸安全轉換成 zh.wikipedia.org/wiki/H
  3. HSTS:鑒於HTTPS遺留的安全隱患,大部分現代瀏覽器已經支持HSTS。對於瀏覽器來說,瀏覽器會檢測是否該網路資源存在於預設定的只使用HTTPS的網站列表,或者是否保存過以前訪問過的只能使用HTTPS的網站記錄,如果是,瀏覽器將強行使用HTTPS方式訪問該網站。

DNS解析

不查DNS,讀取緩存

  • 瀏覽器中的緩存:對於Chrome,緩存查看地址為:chrome://net-internals/#dns
  • 本地hosts文件:以Mac與Linux為例,hosts文件所在路徑為:/etc/hosts。所以有一種翻牆的方式就是修改hosts文件避免GFW對DNS解析的干擾,直接訪問真正IP地址,但已經不能完全生效因為GFW還有根據IP過濾的機制。

發送DNS查找請求

DNS的查詢方式是:按根域名->頂級域名->次級域名->主機名這樣的方式來查找的,對於某個URL,如下所示

查詢步驟為:

  1. 查詢本地DNS伺服器。本地DNS伺服器地址為連接網路時路由器指定的DNS地址,一般為DHCP自動分配的路由器地址,保存在/etc/resolv.conf,而路由器的DNS轉發器將請求轉發到上層ISP的DNS,所以此處本地DNS伺服器是區域網或者運營商的。
  2. 從」根域名伺服器」查到」頂級域名伺服器」的NS記錄和A記錄(IP地址)。世界上一共有十三組根域名伺服器,從A.ROOT-SERVERS.NET一直到M.ROOT-SERVERS.NET,由於已經將這些根域名伺服器的IP地址存放在本地DNS伺服器中。
  3. 從」頂級域名伺服器」查到」次級域名伺服器」的NS記錄和A記錄(IP地址)
  4. 從」次級域名伺服器」查出」主機名」的IP地址

(DNS解析圖示)

google.com為例,下面是在阿里雲實例上作的完整的DNS查詢過程:

  1. 由於本次測試是在阿里雲上的實例進行測試,所以首先從100.100.2.138這個阿里內網DNS伺服器查找到所有根域名伺服器的映射關係。
  2. 訪問根域名伺服器(f.root-servers.net),拿到com頂級域名伺服器的NS記錄與IP地址。
  3. 訪問頂級域名伺服器(e.gtld-servers.net),拿到google.com次級域名伺服器的NS記錄與IP地址。
  4. 訪問次級域名伺服器(ns2.google.com),拿到google.com的IP地址
  5. 所以總的來說,DNS的解析是一個逐步縮小範圍的查找過程。

建立HTTPS、TCP連接

確定發送目標

拿到IP之後,還需要拿到那台伺服器的MAC地址才行,在乙太網協議中規定,同一區域網中的一台主機要和另一台主機進行直接通信,必須要知道目標主機的MAC地址。所以根據ARP(根據IP地址獲取物理地址的一個TCP/IP協議)獲取到MAC地址之後保存到本地ARP緩存之後與目標主機準備開始通信。具體細節參見維基百科DHCH/ARP。

建立TCP連接

為什麼握手一定要是三次?

TCP三次握手

  • 第一次與第二次握手完成意味著:A能發送請求到B,並且B能解析A的請求
  • 第二次與第三次握手完成意味著:A能解析B的請求,並且B能發送請求到A

這樣就保證了A與B之間既能相互發送請求也能相互接收解析請求。同時避免了因為網路延遲產生的重複連接問題,比如A發送一次連接請求但網路延遲導致這次請求是在A重發連接請求並完成與B通信之後的,有三次握手的話,B返回的建立請求A就不會理睬了。

短連接與長連接?

(短連接過程演示)

上圖是一個短連接的過程演示,對於長連接,A與B完成一次讀寫之後,它們之間的連接並不會主動關閉,後續的讀寫操作會繼續使用這個連接。另外,由於長連接的實現比較困難,需要要求長連接在沒有數據通信時,定時發送數據包(心跳),以維持連接狀態,並且長連接對於伺服器的壓力也會很大,所以推送服務對於一般的開發者是非常難以實現的,這樣的話就出現了很多不同的大型廠商提供的消息推送服務。

進行TLS加密過程

  • Hello – 握手開始於客戶端發送Hello消息。包含服務端為了通過SSL連接到客戶端的所有信息,包括客戶端支持的各種密碼套件和最大SSL版本。伺服器也返回一個Hello消息,包含客戶端需要的類似信息,包括到底使用哪一個加密演算法和SSL版本。
  • 證書交換 – 現在連接建立起來了,伺服器必須證明他的身份。這個由SSL證書實現,像護照一樣。SSL證書包含各種數據,包含所有者名稱,相關屬性(域名),證書上的公鑰,數字簽名和關於證書有效期的信息。客戶端檢查它是不是被CA驗證過的。注意伺服器被允許需求一個證書去證明客戶端的身份,但是這個只發生在敏感應用。
  • 密鑰交換 – 先使用RSA非對稱公鑰加密演算法(客戶端生成一個對稱密鑰,然後用SSL證書裡帶的伺服器公鑰將改對稱密鑰加密。隨後發送到服務端,服務端用伺服器私鑰解密,到此,握手階段完成。)或者DH交換演算法在客戶端與服務端雙方確定一將要使用的密鑰,這個密鑰是雙方都同意的一個簡單,對稱的密鑰,這個過程是基於非對稱加密方式和伺服器的公鑰/私鑰的。
  • 加密通信 – 在伺服器和客戶端加密實際信息是用到對稱加密演算法,用哪個演算法在Hello階段已經確定。對稱加密演算法用對於加密和解密都很簡單的密鑰,這個密鑰是基於第三步在客戶端與服務端已經商議好的。與需要公鑰/私鑰的非對稱加密演算法相反。

服務端的處理

靜態緩存、CDN

為了優化網站訪問速度並減少伺服器壓力,通常將html、js、css、文件這樣的靜態文件放在獨立的緩存伺服器或者部署在類似Amazon CloudFront的CDN雲服務上,然後根據緩存過期配置確定本次訪問是否會請求源伺服器來更新緩存。

負載均衡

負載均衡具體實現有多種,有直接基於硬體的F5,有操作系統傳輸層(TCP)上的 LVS,也有在應用層(HTTP)實現的反向代理(也叫七層代理),下面簡單介紹一下最後者。

在請求發送到真正處理請求的伺服器之前,還需要將請求路由到適合的伺服器上,一個請求被負載均衡器拿到之後,需要做一些處理,比如壓縮請求(在nginx中gzip壓縮格式是默認配置在nginx.conf內的,所以默認開啟,如果不對數據量要求特別精細的話,默認配置完全可以滿足基本需求)、接收請求(接收完畢後才發給Server,提高Server處理效率),然後根據預定的路由演算法,將此次請求發送到某個後台伺服器上。

其中需要提到的一點是反向代理,先理解一下正向代理的原理:正向代理是將自己要訪問的資源告訴Proxy,讓Proxy幫你拿到數據返回給你,Proxy服務於Client,常用於翻牆和跨許可權操作。反向代理也是將自己要訪問的資源告訴Proxy,讓Proxy幫你拿到數據返回給你,但是Proxy會將請求接受完畢之後發送給某一合適的Server,這個時候Client是不知道是根據什麼規則並且也不知道最後是哪一個Server服務於它的且Proxy服務於Server,所以叫反向代理,常用於負載均衡、安全控制。

(正向代理和反向代理)

伺服器的處理

對於HTTPD(HTTP Daemon)在伺服器上部署,最常見的 HTTPD 有 Linux 上常用的 Apache 和 Nginx。對於Java平台來說,Tomcat是Spring Boot也會默認選用的Servlet容器實現,以Tomcat為例,對於請求的處理如下:

  1. 請求到達Tomcat啟動時監聽的TCP埠。
  2. 解析請求中的各種信息之後創建一個Request對象並填充那些有可能被所引用的Servlet使用的信息,如參數,頭部、cookies、查詢字元串等。
  3. 創建一個Response對象,所引用的Servlet使用它來給客戶端發送響應。
  4. 調用Servlet的service方法,並傳入Request和Response對象。這裡Servlet會從Request對象取值,給Response寫值。
  5. 根據我們自己寫的Servlet程序或者框架攜帶的Servlet類做進一步的處理(業務處理、請求的進一步處理)
  6. 最後根據Servlet返回的Response生成相應的HTTP響應報文。

瀏覽器的渲染

瀏覽器的功能是從伺服器上取回你想要的資源,然後展示在瀏覽器窗口當中。資源通常是 HTML 文件,也可能是 PDF,圖片,或者其他類型的內容。也可以顯示其他類型的插件(瀏覽器擴展)。例如顯示PDF使用PDF瀏覽器插件。資源的位置通過用戶提供的 URI(Uniform Resource Identifier) 來確定。

瀏覽器解釋和展示 HTML 文件的方法,在 HTML 和 CSS 的標準中有詳細介紹。這些標準由 Web 標準組織 W3C(World Wide Web Consortium) 維護。

圖片來自:http://t.cn/hbvnTt

下面會以Chrome中使用的瀏覽器引擎Webkit為例,根據上圖來簡單介紹瀏覽器的渲染。具體解析、渲染會涉及到非常多的細節,請參考HTML5渲染規範和對應的頁面GPU渲染實現。

HTML解析

瀏覽器拿到具體的HTML文檔之後,需要調用瀏覽器中使用的瀏覽器引擎中處理HTML的工具(HTML Parser)來將HTML文檔解析成為DOM樹,將以便外部介面(JS)調用。

  • 文檔內容解析:將一大串字元串解析為DOM之前需要從中分析出結構化的信息讓HTML解析器可以很方便地提取數據進行其他操作,所以對於文檔內容的解析是第一步。解析器有兩個處理過程——詞法分析(將字元串切分成符合特定語法規範的符號)與語法分析(根據符合語法規範的符號構建對應該文檔的語法樹)。
  • HTML解析:根據HTML語法,將HTML標記到語法樹上構建成DOM(Document Object Model)。

CSS解析

  • 根據CSS詞法和句法分析CSS文件和<style>標籤包含的內容以及style屬性的值
  • 每個CSS文件都被解析成一個樣式表對象(StyleSheet object),這個對象里包含了帶有選擇器的CSS規則,和對應CSS語法的對象

頁面渲染

解析完成後,瀏覽器引擎會通過DOM樹和CSS Rule樹來構造渲染樹。渲染樹的構建會產生Layout,Layout是定位坐標和大小,是否換行,各種position, overflow, z-index屬性的集合,也就是對各個元素進行位置計算、樣式計算之後的結果。

接下來,根據渲染樹對頁面進行渲染(可以理解為「畫」元素)。

當然,將這個渲染的過程完成並顯示到屏幕上會涉及到顯卡的繪製,顯存的修改,有興趣的讀者可以深入了解。本文只是將對於我們軟體開發者來說需要了解的部分進行總結,還有很多過程與機制沒有提到,github上的這個項目期待你的貢獻。How does HTTPS actually work? | Robert Heaton?

robertheaton.com圖標當然,將這個渲染的過程完成並顯示到屏幕上會涉及到顯卡的繪製,顯存的修改,有興趣的讀者可以深入了解。本文只是將對於我們軟體開發者來說需要了解的部分進行總結,還有很多過程與機制沒有提到,github上的這個項目期待你的貢獻。

參考

  • github.com/skyline75489
  • achuan.me/2017/03/01/20
  • zh.wikipedia.org/wiki/H
  • ruanyifeng.com/blog/201
  • https://technet.microsoft.com/en-us/library/cc772774(v=ws.10).aspx.aspx)
  • ruanyifeng.com/blog/201
  • github.com/jawil/blog/i
  • robertheaton.com/2014/0

更多精彩洞見,請關注微信公眾號:思特沃克 文/ThoughtWorks王天一

推薦閱讀:

從URL輸入到頁面展現的過程
網址後面用?/&等符號引導的語句有什麼功能?
【SEO學習之路】網址URL如何定義SEO效果最優?
編碼表單數據時為什麼要將%20替換為+?

TAG:DevOps | HTTP | URL |