對瀏覽器原理有過了解的一定不會陌生這篇神文《How Browsers Work》中文翻譯:瀏覽器原理:新式網(wǎng)絡瀏覽器幕后揭秘般渡。另外還有一篇 《What really happens when you navigate to a URL》懒豹。大神寫的東西很長很復雜,閱讀成本雖然大驯用,但能學到東西脸秽。所以,我也試著用自己的理解去寫一寫蝴乔,算是做個鞏固记餐。里面有很多參考,如涉及版權薇正,侵權刪片酝!表述有誤,請指正挖腰!
前端為什么要研究渲染原理钠怯?
像素完美(Pixel Perfection)、分辨率無關(Resolution Independent)和多平臺體驗一致性是設計師的追求曙聂。而訪問性(Accessability)晦炊、加載性能和重構(gòu)靈活性則是前端工程師必懂的技能。最重要的加載性能又與瀏覽器渲染機制深深掛鉤宁脊,所以只有弄明白了瀏覽器背后的渲染機制断国,才能在日常的前端開發(fā)中明白如何進行性能優(yōu)化。
還有就是像類似:
從輸入 URL 到頁面加載完成的過程中都發(fā)生了什么事情榆苞?
從按下鍵盤到屏幕上出現(xiàn)字符稳衬,中間都發(fā)生了什么事情?
用戶反應網(wǎng)站卡坐漏,請問都有哪些可能性薄疚,以及解決方法碧信?
這樣的問題面試官都是比較喜歡問的,今天嘗試來詳細說一下這個過程街夭。
從輸入 URL 到頁面加載完成的過程中都發(fā)生了什么砰碴?
簡單路徑線:
- 鍵盤或觸屏輸入URL并回車確認
- URL解析/DNS解析查找域名IP地址
- 網(wǎng)絡連接發(fā)起HTTP請求
- HTTP報文傳輸過程
- 服務器接收數(shù)據(jù)
- 服務器響應請求/MVC
- 服務器返回數(shù)據(jù)
- 客戶端接收數(shù)據(jù)
- 瀏覽器加載/渲染頁面
- 打印繪制輸出
實際上并沒有這么簡單,下面說說它的詳細路徑線:
1. 鍵盤或觸屏輸入URL并回車確認
當然故事其實并不是從輸入一個URL或抓著鼠標點擊一個鏈接開始的板丽,事情的開端要追溯到服務器啟動監(jiān)聽服務的時候呈枉,在某個未知的時刻,一臺機房里普普通通的服務器埃碱,加上電猖辫,啟動了操作系統(tǒng),隨著操作系統(tǒng)的就緒砚殿,服務器啟動了 http 服務進程啃憎,這個 http 服務的守護進程(daemon),可能是 Apache似炎、Nginx荧飞、IIS、Lighttpd中的一個名党,不管怎么說,這個 http 服務進程開始定位到服務器上的 www 文件夾(網(wǎng)站根目錄)挠轴,一般是位于 /var/www 传睹,然后啟動了一些附屬的模塊,例如 php岸晦,或者欧啤,使用 fastcgi 方式連接到 php 的 fpm 管理進程,然后启上,向操作系統(tǒng)申請了一個 tcp 連接邢隧,然后綁定在了 80 端口,調(diào)用了 accept 函數(shù)冈在,開始了默默的監(jiān)聽倒慧,監(jiān)聽著可能來自位于地球任何一個地方的請求,隨時準備做出響應包券。這個時候纫谅,典型的情況下,機房里面應該還有一個數(shù)據(jù)庫服務器溅固,或許付秕,還有一臺緩存服務器,如果對于流量巨大的網(wǎng)站侍郭,那么動態(tài)腳本的解釋器可能還有單獨的物理機器來跑询吴,如果是中小的站點掠河,那么,上述的各色服務猛计,甚至都可能在一臺物理機上唠摹,這些服務監(jiān)聽之間的關系,可以通過自己搭建一次 Apache PHP MySQL 環(huán)境來了解一下有滑,不管怎么說跃闹,他們做好了準備,靜候差遣毛好。
然后是開始鍵盤或手機觸屏輸入URL望艺,然后通過某種機制傳到CPU(過程略),CPU進行內(nèi)部處理(過程略)肌访,處理完后找默,再從CPU傳到操作系統(tǒng)內(nèi)核(過程略),然后再由操作系統(tǒng)GUI傳到瀏覽器吼驶,再由瀏覽器到瀏覽器內(nèi)核惩激。這個過程因為涉及很多底層的知識,自己也只是了解皮毛蟹演,過程這里不多講了风钻,具體請參考我的另一篇博客《字符集歷史和亂碼問題》和以下書籍:
《編碼》
《操作系統(tǒng)概念》
《CPU自制入門》
《計算機體系結(jié)構(gòu)》
《Linux內(nèi)核設計與實現(xiàn)》
《精通Linux設備驅(qū)動程序開發(fā)》
《計算機體系結(jié)構(gòu):量化研究方法》
《計算機組成與設計:硬件/軟件接口》
上面一步操作系統(tǒng) GUI 會將輸入事件傳遞到瀏覽器中,在這過程中酒请,瀏覽器可能會做一些預處理骡技,甚至已經(jīng)在智能匹配所有可能的URL了,他會從歷史記錄羞反,書簽等地方布朦,找到已經(jīng)輸入的字符串可能對應的URL,來預估所輸入字符對應的網(wǎng)站昼窗,然后給出智能提示是趴,比如輸入了「ba」,根據(jù)之前的歷史發(fā)現(xiàn) 90% 的概率會訪問「www.baidu.com 」澄惊,因此就會在輸入回車前就馬上開始建立 TCP 鏈接了唆途。對于 Chrome這種變態(tài)的瀏覽器,他甚至會直接從緩存中把網(wǎng)頁渲染出來掸驱,就是說窘哈,你還沒有按下「回車」鍵,頁面就已經(jīng)出來了亭敢,再比如Chrome會在瀏覽器啟動時預先查詢10個你有可能訪問的域名等等滚婉,這里面還有很多其它策略,不詳細講了帅刀。感興趣的推薦閱讀 High Performance Networking in Chrome让腹。
2. URL 解析/DNS 查詢
接著是輸入 URL 「回車」后远剩,這時瀏覽器會對 URL 進行檢查,這里需要對URL有個回顧骇窍,請見百科《URL》瓜晤,完整的URL由幾個部分構(gòu)成:
協(xié)議、網(wǎng)絡地址腹纳、資源路徑痢掠、文件名、動態(tài)參數(shù)
協(xié)議/模式(scheme)是從該計算機獲取資源的方式嘲恍,一般有Http足画、Https、Ftp佃牛、File淹辞、Mailto浩姥、Telnet枫笛、News等協(xié)議桥言,不同協(xié)議有不同的通訊內(nèi)容格式讨便,協(xié)議主要作用是告訴瀏覽器如何處理將要打開的文件;
網(wǎng)絡地址指示該連接網(wǎng)絡上哪一臺計算機(服務器)畏铆,可以是域名或者IP地址默伍,域名或IP地址后面有時還跟一個冒號和一個端口號传货;
端口號如果地址不包含端口號惫东,根據(jù)協(xié)議的類型會確定一個默認端口號莉给。端口號之于計算機就像窗口號之于銀行,一家銀行有多個窗口凿蒜,每個窗口都有個號碼,不同窗口可以負責不同的服務胁黑。端口只是一個邏輯概念废封,和計算機硬件沒有關系。一般如果你的端口號就是默認的丧蘸,那么url是不需要輸入端口號的漂洋,但如果你更改了默認端口號,你就必須要在url后輸入新端口號才能正常訪問力喷。例如:http協(xié)議默認端口號是80刽漂。如果你輸入的url是http://www.zhihu.com:8080/ ,那表示不使用默認的端口號弟孟,而使用指定的端口號8080贝咙。如果使用的就是默認端口號那么輸入http://www.zhihu.com:80 和http://www.zhihu.com是一樣的。有個特殊情況有所不同拂募,比如本地IP 127.0.0.1 其實走的是 loopback庭猩,和網(wǎng)卡設備沒關系窟她。
資源路徑指示從服務器上獲取哪一項資源的等級結(jié)構(gòu)路徑,以斜線/
分隔蔼水;
文件名一般是需要真正訪問的文件震糖,有時候,URL以斜杠“/”結(jié)尾趴腋,而隱藏了文件名吊说,在這種情況下,URL引用路徑中最后一個目錄中的默認文件(通常對應于主頁)优炬,這個文件常被稱為 index.html 或 default.htm颁井。
動態(tài)參數(shù)有時候路徑后面會有以問號?
開始的參數(shù),這一般都是用來傳送對服務器上的數(shù)據(jù)庫進行動態(tài)詢問時所需要的參數(shù)穿剖,有時候沒有蚤蔓,很多為了seo優(yōu)化,都已處理成偽靜態(tài)了糊余。要注意區(qū)分url和路由的區(qū)別秀又。
URL完整格式為:協(xié)議://用戶名:密碼@子域名.域名.頂級域名:端口號/目錄/文件名.文件后綴?參數(shù)=值#標志
例如:https://www.zhihu.com/question/55998388/answer/166987812
協(xié)議部分:https
網(wǎng)絡地址:www.zhihu.com(依次為 子/三級域名.二級域名.頂/一級域名)
資源路徑:/question/55998388/answer/166987812
瀏覽器對 URL 進行檢查時首先判斷協(xié)議,如果是 http/https 就按照 Web 來處理贬芥,另外還會對 URL 進行安全檢查吐辙,然后直接調(diào)用瀏覽器內(nèi)核中的對應方法,接下來是對網(wǎng)絡地址進行處理蘸劈,如果地址不是一個IP地址而是域名則通過DNS(域名系統(tǒng))將該地址解析成IP地址昏苏。IP地址對應著網(wǎng)絡上一臺計算機,DNS服務器本身也有IP威沫,你的網(wǎng)絡設置包含DNS服務器的IP贤惯。 例如:www.zhihu.com域名請求對應獲得的IP是 116.211.167.187。DNS 在解析域名的時候有兩種方式:遞歸查詢和迭代查詢棒掠,
遞歸查詢的流程如下:
一般來說孵构,瀏覽器會首先查詢瀏覽器緩存(DNS 在各個層級都有緩存的,相應的烟很,緩存當然有過期時間颈墅,Time to live),如果沒有找到雾袱,就會檢查系統(tǒng)緩存恤筛,檢查本地硬盤的hosts文件,這個文件保存了一些以前訪問過的網(wǎng)站的域名和IP對應的數(shù)據(jù)芹橡。它就像是一個本地的數(shù)據(jù)庫毒坛。如果找到就可以直接獲取目標主機的IP地址了(注意這個地方存在安全隱患,如果有病毒把一些常用的域名林说,修改 hosts 文件粘驰,指向一些惡意的IP屡谐,那么瀏覽器也會不加判斷的去連接,是的蝌数,這正是很多病毒的慣用手法)愕掏。如果本地hosts也沒有找到的話,則需要再向上層找路由器緩存顶伞,路由器有自己的DNS緩存饵撑,可能就包括了查詢的內(nèi)容;如果還是沒有唆貌,需要接著往上找滑潘,查詢ISP DNS 緩存(本地名稱服務器緩存,就是客戶端電腦TCP/IP參數(shù)中設置的首選DNS服務器锨咙,此解析具有權威性语卤。一般情況下你在不同的地區(qū)或者不同的網(wǎng)絡,如電信酪刀、聯(lián)通粹舵、移動的情況下,轉(zhuǎn)換后的IP地址很可能是不一樣的骂倘,這涉及到負載均衡眼滤,通過DNS解析域名時會將你的訪問分配到不同的入口,先找附近的本地 DNS 服務器去請求解析域名历涝,盡可能保證你所訪問的入口是所有入口中較快的一個诅需,這和CDN還不一樣,比如我們經(jīng)常使用的114.114.114.114或Google的8.8.8.8就是本地名稱服務器)荧库。如果附近的本地DNS服務器還是沒有緩存我們請求的域名記錄的話堰塌,這時候會根據(jù)本地DNS服務器的設置(是否設置轉(zhuǎn)發(fā)器)進行查詢,如果未用轉(zhuǎn)發(fā)模式分衫,則本地名稱服務器再以DNS客戶端的角色發(fā)送與前面一樣的DNS域名查詢請求轉(zhuǎn)發(fā)給上一層场刑。這里可能經(jīng)過一次或者多次轉(zhuǎn)發(fā),從本地名稱服務器到權威名稱服務器再到頂級名稱服務器最后到根名稱服務器丐箩。(順便一提摇邦,根服務器是互聯(lián)網(wǎng)域名解析系統(tǒng)DNS中最高級別的域名服務器恤煞,全球一共13組屎勘,每組都只有一個主根名稱服務器采用同一個IP。注意不是13個居扒,前期是個現(xiàn)在已經(jīng)是集群了概漱,據(jù)說已經(jīng)有上千臺了,好多臺用于負載均衡喜喂,備份等瓤摧,全球有386臺根物理服務器竿裂,被編號為A到M共13個標號。中國包括臺港也持有其中5組14臺輔根服務器或叫鏡像也可以照弥,386臺根服務器總共只使用了13個IP腻异,因此可以抵抗針對其所進行的分布式拒絕服務攻擊DDoS。具體情況可以參看維基百科的 根域名服務器 條目)所以这揣,最終請求到了根服務器后悔常,根服務器查詢發(fā)現(xiàn)我們這個被請求的域名是由類似A或者B這樣的服務器解析的,但是给赞,根服務器并不會送佛送到西地找A或B之類的直接去解析机打,因為它沒有保存全部互聯(lián)網(wǎng)域名記錄,并不直接用于名稱解析片迅,它只是負責頂級名稱服務器(如.com/.cn/.net等)的相關內(nèi)容残邀。所以它會把所查詢得到的被請求的DNS域名中頂級域名所對應的頂級名稱服務器IP地址返回給本地名稱服務器。本地名稱服務器拿到地址后再向?qū)捻敿壝Q服務器發(fā)送與前面一樣的DNS域名查詢請求柑蛇。對應的頂級名稱服務器在收到DNS查詢請求后芥挣,也是先查詢自己的緩存,如果有則直接把對應的記錄項返回給本地名稱服務器唯蝶,然后再由本地名稱服務器返回給DNS客戶端九秀,如果沒有則向本地名稱服務器返回所請求的DNS域名中的二級域名所對應的二級名稱服務器(如baidu.com/qq.com/net.cn等)地址。然后本地名稱服務器繼續(xù)按照前面介紹的方法一次次地向三級(如www.baidu.com/www.qq.com/bbs.taobao.com等)粘我、四級名稱服務器查詢鼓蜒,直到最終的對應域名所在區(qū)域的權威名稱服務器返回最終記錄給本地名稱服務器。同時本地名稱服務器會緩存本次查詢得到的記錄項(每層都應該會緩存)征字。再層層下傳都弹,最后到了我們的DNS客戶端機子,一次 DNS 解析請求就此完成匙姜。如果最終權威名稱服務器都說找不到對應的域名記錄畅厢,則會向本地名稱服務器返回一條查詢失敗的DNS應答報文,這條報文最終也會由本地名稱服務器返回給DNS客戶端氮昧。當然框杜,如果這個權威名稱服務器上配置了指向其它名稱服務器的轉(zhuǎn)發(fā)器,則權威名稱服務器還會在轉(zhuǎn)發(fā)器指向的名稱服務器上進一步查詢袖肥。另外咪辱,如果DNS客戶端上配置了多個DNS服務器,則還會繼續(xù)向其它DNS服務器查詢的椎组。
所以油狂,我們看到DNS的域名解析是遞歸的,遞歸的DNS首先會查看自己的DNS緩存,如果緩存能夠命中专筷,那么就從緩存中把IP地址返回給瀏覽器弱贼,如果找不到對應的域名的IP地址,那么就依此層層向上轉(zhuǎn)發(fā)請求磷蛹,從根域名服務器到頂級域名服務器再到極限域名服務器依次搜索對應目標域名的IP吮旅,最高達到根節(jié)點,找到或者全部找不到為止味咳。然后把找到的這個域名對應的 nameserver 的地址拿到鸟辅,再向這個 namserver 去請求域名對應的IP,最后把這個IP地址返回給瀏覽器莺葫,在這個遞歸查詢的過程中匪凉,對于瀏覽器來說是透明的,如果DNS客戶端的本地名稱服務器不能解析的話捺檬,則后面的查詢都會以本地名稱服務器為中心再层,全交由本地名稱服務器代替DNS客戶端進行查詢,DNS客戶端只是發(fā)出原始的域名查詢請求報文堡纬,然后就一直處于坐等狀態(tài)聂受,直到本地名稱服務器最終從權威名稱服務器得到了正確的IP地址查詢結(jié)果并返回給它。雖然遞歸查詢是默認的DNS查詢方式烤镐,但是如果有以下情況發(fā)生的話蛋济,則會使用迭代的查詢方式進行。
情況一:DNS客戶端的請求報文中沒有申請使用遞歸查詢炮叶,即在DNS請求報頭部的RD字段沒有置1碗旅。
情況二:DNS客戶端的請求報文中申請使用的是遞歸查詢(也就是RD字段置1了),但在所配置的本地名稱服務器上是禁用遞歸查詢了(即在應答DNS報文頭部的RA字段置0)镜悉。
迭代查詢的流程如下:
開始也是從瀏覽器緩存到系統(tǒng)緩存到路由緩存祟辟,如果還是沒找到則客戶端向本機配置的本地名稱服務器(在此僅以首先DNS服務器為例進行介紹,其它備用DNS服務器的解析流程完全一樣)發(fā)出DNS域名查詢請求侣肄。本地名稱服務器收到請求后旧困,先查詢本地的緩存,如果有該域名的記錄項稼锅,則本地名稱服務器就直接把查詢的結(jié)果返回給客戶端吼具;如果本地緩存中沒有該域名的記錄,則向DNS客戶端返回一條DNS應答報文矩距,報文中會給出一些參考信息拗盒,如本地名稱服務器上的根名稱服務器地址等。DNS客戶端在收到本地名稱服務器的應答報文后剩晴,會根據(jù)其中的根名稱服務器地址信息锣咒,向?qū)母Q服務器再次發(fā)出與前面一樣的DNS查詢請求報文。根名稱服務器在收到DNS查詢請求報文后赞弥,通過查詢自己的DNS數(shù)據(jù)庫得到請求DNS域名中頂級域名所對應的頂級名稱服務器信息毅整,然后以一條DNS應答報文返回給DNS客戶端。DNS客戶端根據(jù)來自根名稱服務器應答報文中的對應頂級名稱服務器地址信息绽左,向該頂級名稱服務器發(fā)出與前面一樣的DNS查詢請求報文悼嫉。頂級名稱服務器在收到DNS查詢請求后,先查詢自己的緩存拼窥,如果有請求的DNS域名的記錄項戏蔑,則直接把對應的記錄項返回給DNS客戶端,否則通過查詢后把對應域名中二級域名所對應的二級名稱服務器地址信息以一條DNS應答報文返回給DNS客戶端鲁纠。然后DNS客戶端繼續(xù)按照前面介紹的方法一次次地向三級总棵、四級名稱服務器查詢,直到最終的權威名稱服務器返回到最終的記錄改含。如果權威名稱服務器也找不到對應的域名記錄情龄,則會向DNS客戶端返回一條查詢失敗的DNS應答報文。當然捍壤,如果這個權威名稱服務器上配置了指向其它名稱服務器的轉(zhuǎn)發(fā)器骤视,則權威名稱服務器還會在轉(zhuǎn)發(fā)器指向的名稱服務器上進一步查詢。另外鹃觉,如果DNS客戶端上配置了多個DNS服務器专酗,則還會繼續(xù)向其它DNS服務器查詢。
所以盗扇,我們發(fā)現(xiàn)在遞歸查詢中后面的查詢工作是由本地名稱服務器替代DNS客戶端進行的(以“本地名稱服務器”為中心)祷肯,只需要本地名稱服務器向DNS客戶端返回最終的查詢結(jié)果即可。而DNS迭代查詢的所有查詢工作則全部是DNS客戶端自己進行(以“DNS客戶端”自己為中心)疗隶。
DNS遞歸查詢和迭代查詢的區(qū)別躬柬?
遞歸查詢是以本地名稱服務器為中心的,是DNS客戶端和服務器之間的查詢活動抽减,遞歸查詢的過程中“查詢的遞交者” 一直在更替允青,其結(jié)果是直接告訴DNS客戶端需要查詢的網(wǎng)站目標IP地址。
迭代查詢則是DNS客戶端自己為中心的卵沉,是各個服務器和服務器之間的查詢活動颠锉,迭代查詢的過程中“查詢的遞交者”一直沒變化,其結(jié)果是間接告訴DNS客戶端另一個DNS服務器的地址史汗。
舉個例子來說琼掠,一次選修課上你碰到了你的女神,但你只知道她的名字并不知道她的電話停撞,于是在手機通訊錄里找瓷蛙,沒找到悼瓮,然后你回到寢室把她名字告訴了一個很仗義的哥們,讓他幫你找艰猬,這個哥們兒(本地名稱服務器)二話沒說横堡,開始替你查(此處完成了一次遞歸查詢,即問詢的人由你變成了你的哥們)冠桃。然后你哥們帶著名字去問了學院大四的學長命贴,學長一看,我擦食听,這姑娘我認識啊胸蛛,于是在手機里找到了她的電話告訴了你哥們,你哥們回來告訴了你樱报,你于是知道了她的電話(這里一次遞歸查詢結(jié)束)葬项。還有一種可能你哥們跑去問學長,學長也沒她電話迹蛤,但學長告訴你哥們玷室,這姑娘是xx系的;然后你哥們兒馬不停蹄又問了xx系的辦公室主任助理同學笤受,助理同學說是xx系yy班的穷缤,然后很仗義的哥們兒去xx系yy班的班長那里取到了該女孩的電話(此處完成若干次迭代查詢,即問詢的人一直是你哥們不變箩兽,但反復更替的是問詢對象)津肛。最后,他把號碼交到了你手里汗贫,完成整個查詢過程身坐。
擴展閱讀:
什么是DNS劫持?
什么是301重定向落包?與301重定向設置教程
電腦上不了網(wǎng)將DNS改為114.114.114.114或8.8.8.8可以解決或加快網(wǎng)速的原理是什么部蛇?
局域網(wǎng) IP 和公網(wǎng) IP 有何差別?
根域名服務器的作用是什么咐蝇?全球 13 組根域名服務器中有 10 組在美國涯鲁,意味著什么?
遞歸和迭代的區(qū)別有序?
3. 應用層客戶端發(fā)送HTTP請求
互聯(lián)網(wǎng)內(nèi)各網(wǎng)絡設備間的通信都遵循TCP/IP協(xié)議抹腿,利用TCP/IP協(xié)議族進行網(wǎng)絡通信時,會通過分層順序與對方進行通信旭寿。分層由高到低分別為:應用層警绩、傳輸層、網(wǎng)絡層盅称、數(shù)據(jù)鏈路層肩祥。發(fā)送端從應用層往下走后室,接收端從數(shù)據(jù)鏈路層網(wǎng)上走。如圖所示:
從上面的步驟中得到 IP 地址后混狠,瀏覽器會開始構(gòu)造一個 HTTP 請求岸霹,應用層客戶端向服務器端發(fā)送的HTTP請求包括:請求報頭和請求主體兩個部分,其中請求報頭(request header)包含了至關重要的信息檀蹋,包括請求的方法(GET / POST和不常用的PUT / DELETE以及更不常用的HEAD / OPTION / TRACE,一般的瀏覽器只能發(fā)起 GET 或者 POST 請求)云芦、目標url俯逾、遵循的協(xié)議(HTTP / HTTPS / FTP…),返回的信息是否需要緩存舅逸,以及客戶端是否發(fā)送Cookie等信息桌肴。需要注意的是,因為 HTTP 請求是純文本格式的琉历,所以在 TCP 的數(shù)據(jù)段中可以直接分析 HTTP 文本的坠七。
4. 傳輸層TCP傳輸報文
當應用層的 HTTP 請求準備好后,瀏覽器會在傳輸層發(fā)起一條到達服務器的 TCP 連接旗笔,位于傳輸層的TCP協(xié)議為傳輸報文提供可靠的字節(jié)流服務彪置。它為了方便傳輸,將大塊的數(shù)據(jù)分割成以報文段為單位的數(shù)據(jù)包進行管理蝇恶,并為它們編號拳魁,方便服務器接收時能準確地還原報文信息。TCP協(xié)議通過“三次握手”等方法保證傳輸?shù)陌踩煽看榛 潘懊!叭挝帐帧钡倪^程是,發(fā)送端先發(fā)送一個帶有SYN(synchronize)標志的數(shù)據(jù)包給接收端贿衍,在一定的延遲時間內(nèi)等待接收的回復授舟。接收端收到數(shù)據(jù)包后,傳回一個帶有SYN/ACK標志的數(shù)據(jù)包以示傳達確認信息贸辈。接收方收到后再發(fā)送一個帶有ACK標志的數(shù)據(jù)包給接收端以示握手成功释树。在這個過程中,如果發(fā)送端在規(guī)定延遲時間內(nèi)沒有收到回復則默認接收方?jīng)]有收到請求擎淤,而再次發(fā)送躏哩,直到收到回復為止。
這里需要談一下 TCP 的 Head-of-line blocking 問題:假設客戶端的發(fā)送了 3 個 TCP 片段(segments)揉燃,編號分別是 1扫尺、2、3炊汤,如果編號為 1 的包傳輸時丟了正驻,即便編號 2 和 3 已經(jīng)到達也只能等待弊攘,因為 TCP 協(xié)議需要保證順序,這個問題在 HTTP pipelining 下更嚴重姑曙,因為 HTTP pipelining 可以讓多個 HTTP 請求通過一個 TCP 發(fā)送襟交,比如發(fā)送兩張圖片,可能第二張圖片的數(shù)據(jù)已經(jīng)全收到了伤靠,但還得等第一張圖片的數(shù)據(jù)傳到捣域。為了解決 TCP 協(xié)議的性能問題,Chrome 團隊提出了 QUIC 協(xié)議宴合,它是基于 UDP 實現(xiàn)的可靠傳輸焕梅,比起 TCP,它能減少很多來回(round trip)時間卦洽,還有前向糾錯碼(Forward Error Correction)等功能贞言。目前 Google Plus、 Gmail阀蒂、Google Search该窗、blogspot、Youtube 等幾乎大部分 Google 產(chǎn)品都在使用 QUIC蚤霞,可以通過chrome://net-internals/#spdy 頁面來發(fā)現(xiàn)酗失。另外,瀏覽器對同一個域名有連接數(shù)限制昧绣,大部分是 6级零,但并非將這個連接數(shù)改大后就會提升性能,Chrome 團隊有做過實驗滞乙,發(fā)現(xiàn)從 6 改成 10 后性能反而下降了奏纪,造成這個現(xiàn)象的因素有很多,如建立連接的開銷斩启、擁塞控制等問題序调,而像 SPDY、HTTP 2.0 協(xié)議盡管只使用一個 TCP 連接來傳輸數(shù)據(jù)兔簇,但性能反而更好发绢,而且還能實現(xiàn)請求優(yōu)先級。
5. 網(wǎng)絡層IP協(xié)議查詢MAC地址
IP協(xié)議的作用是把TCP分割好的各種數(shù)據(jù)包封裝到IP包里面?zhèn)魉徒o接收方垄琐。而要保證確實能傳到接收方還需要接收方的MAC地址边酒,也就是物理地址才可以。IP地址和MAC地址是一一對應的關系狸窘,一個網(wǎng)絡設備的IP地址可以更換墩朦,但是MAC地址一般是固定不變的。ARP協(xié)議可以將IP地址解析成對應的MAC地址翻擒。當通信的雙方不在同一個局域網(wǎng)時氓涣,需要多次中轉(zhuǎn)才能到達最終的目標牛哺,在中轉(zhuǎn)的過程中需要通過下一個中轉(zhuǎn)站的MAC地址來搜索下一個中轉(zhuǎn)目標。
6. 數(shù)據(jù)到達數(shù)據(jù)鏈路層
在找到對方的MAC地址后劳吠,已被封裝好的IP包再被封裝到數(shù)據(jù)鏈路層的數(shù)據(jù)幀結(jié)構(gòu)中引润,將數(shù)據(jù)發(fā)送到數(shù)據(jù)鏈路層傳輸,再通過物理層的比特流送出去痒玩。這時淳附,客戶端發(fā)送請求的階段結(jié)束。
這些分層的意義在于分工合作蠢古,數(shù)據(jù)鏈路層通過 CSMA/CD 協(xié)議保證了相鄰兩臺主機之間的數(shù)據(jù)報文傳遞奴曙,而網(wǎng)絡層的 IP 數(shù)據(jù)包通過不同子網(wǎng)之間的路由器的路由算法和路由轉(zhuǎn)發(fā),保證了互聯(lián)網(wǎng)上兩臺遙遠主機之間的點對點的通訊便瑟,不過這種傳輸是不可靠缆毁,于是可靠性就由傳輸層的 TCP 協(xié)議來保證番川,TCP 通過慢開始到涂,乘法減小等手段來進行流量控制和擁塞避免,同時提供了兩臺遙遠主機上進程到進程的通信两入,最終保證了 HTTP 的請求頭能夠被遠方的服務器上正在監(jiān)聽的 HTTP 服務器進程收到劈彪,終于蟋字,數(shù)據(jù)包在跳與跳之間被拆了又封裝,在子網(wǎng)與子網(wǎng)之間被轉(zhuǎn)發(fā)了又轉(zhuǎn)發(fā)屿讽,最后進入了服務器的操作系統(tǒng)的緩沖區(qū),服務器的操作系統(tǒng)由此給正在被阻塞住的 accept 函數(shù)一個返回吠裆,將他喚醒伐谈。
7. 服務器接收數(shù)據(jù)
接收端的服務器在鏈路層接收到數(shù)據(jù)包,再層層向上直到應用層试疙。這過程中包括在傳輸層通過TCP協(xié)議將分段的數(shù)據(jù)包重新組成原來的HTTP請求報文诵棵。
8. 服務器響應請求并返回相應文件
服務接收到客戶端發(fā)送的HTTP請求后,服務器上的的 http 監(jiān)聽進程會得到這個請求祝旷,然后一般情況下會啟動一個新的子進程去處理這個請求履澳,同時父進程繼續(xù)監(jiān)聽。http 服務器首先會查看重寫規(guī)則怀跛,然后如果請求的文件是真實存在距贷,例如一些圖片,或 html吻谋、css忠蝗、js 等靜態(tài)文件,則會直接把這個文件返回漓拾,如果是一個動態(tài)的請求什湘,那么會根據(jù) url 重寫模塊的規(guī)則长赞,把這個請求重寫到一個 rest 風格的 url 上,然后根據(jù)動態(tài)語言的腳本闽撤,來決定調(diào)用什么類型的動態(tài)文件腳本解釋器來處理這個請求得哆。
我們以 php 語言為例來說的話,請求到達一個 php 的 mvc 框架之后哟旗,框架首先應該會初始化一些環(huán)境的參數(shù)贩据,例如遠端 ip,請求參數(shù)等等闸餐,然后根據(jù)請求的 url 送到一個路由器類里面去匹配路由饱亮,路由由上到下逐條匹配,一旦遇到 url 能夠匹配的上舍沙,而且請求的方法也能夠命中的話近上,那么請求就會由這個路由所定義的處理方法去處理。
請求進入處理函數(shù)之后拂铡,如果客戶端所請求需要瀏覽的內(nèi)容是一個動態(tài)的內(nèi)容壹无,那么處理函數(shù)會相應的從數(shù)據(jù)源里面取出數(shù)據(jù),這個地方一般會有一個緩存感帅,例如 memcached 來減小 db 的壓力斗锭,如果引入了 orm 框架的話,那么處理函數(shù)直接向 orm 框架索要數(shù)據(jù)就可以了失球,由 orm 框架來決定是使用內(nèi)存里面的緩存還是從 db 去取數(shù)據(jù)岖是,一般緩存都會有一個過期的時間,而 orm 框架也會在取到數(shù)據(jù)回來之后实苞,把數(shù)據(jù)存一份在內(nèi)存緩存中的豺撑。
orm 框架負責把面向?qū)ο蟮恼埱蠓g成標準的 sql 語句,然后送到后端的 db 去執(zhí)行黔牵,db 這里以 mysql 為例的話聪轿,那么一條 sql 進來之后,db 本身也是有緩存的荧止,不過 db 的緩存一般是用 sql 語言 hash 來存取的屹电,也就是說,想要緩存能夠命中跃巡,除了查詢的字段和方法要一樣以外危号,查詢的參數(shù)也要完全一模一樣才能夠使用 db 本身的查詢緩存,sql 經(jīng)過查詢緩存器素邪,然后就會到達查詢分析器外莲,在這里,db 會根據(jù)被搜索的數(shù)據(jù)表的索引建立情況,和 sql 語言本身的特點偷线,來決定使用哪一個字段的索引磨确,值得一提的是,即使一個數(shù)據(jù)表同時在多個字段建立了索引声邦,但是對于一條 sql 語句來說乏奥,還是只能使用一個索引,所以這里就需要分析使用哪個索引效率最高了亥曹,一般來說邓了,sql 優(yōu)化在這個點上也是很重要的一個方面。
sql 由 db 返回結(jié)果集后媳瞪,再由 orm 框架把結(jié)果轉(zhuǎn)換成模型對象骗炉,然后由 orm 框架進行一些邏輯處理蛇受,把準備好的數(shù)據(jù)句葵,送到視圖層的渲染引擎去渲染,渲染引擎負責模板的管理兢仰,字段的友好顯示乍丈,也包括負責一些多國語言之類的任務。對于一條請求在 mvc 中的生命周期旨别,可以參考這里诗赌,臨摹了一個 PHP MVC 框架汗茄,在視圖層把頁面準備好后秸弛,再從動態(tài)腳本解釋器送回到 http 服務器,由 http 服務器把這些正文加上一個響應頭洪碳,封裝成一個標準的 http 響應包递览,再通過 tcp ip 協(xié)議,送回到客戶機瀏覽器瞳腌。
9)瀏覽器開始處理數(shù)據(jù)信息并渲染頁面
歷經(jīng)千辛萬苦绞铃,我們請求的響應終于成功到達了客戶端的瀏覽器,響應到達瀏覽器之后嫂侍,瀏覽器首先會根據(jù)返回的響應報文里的一個重要信息——狀態(tài)碼儿捧,來做個判斷。如果是 200 開頭的就好辦挑宠,表示請求成功菲盾,直接進入渲染流程,如果是 300 開頭的就要去相應頭里面找 location 域各淀,根據(jù)這個 location 的指引懒鉴,進行跳轉(zhuǎn),這里跳轉(zhuǎn)需要開啟一個跳轉(zhuǎn)計數(shù)器,是為了避免兩個或者多個頁面之間形成的循環(huán)的跳轉(zhuǎn)临谱,當跳轉(zhuǎn)次數(shù)過多之后璃俗,瀏覽器會報錯,同時停止悉默。比如:301表示永久重定向城豁,即請求的資源已經(jīng)永久轉(zhuǎn)移到新的位置。在返回301狀態(tài)碼的同時抄课,響應報文也會附帶重定向的url钮蛛,客戶端接收到后將http請求的url做相應的改變再重新發(fā)送。如果是 400 開頭或者 500 開頭的狀態(tài)碼剖膳,瀏覽器也會給出一個錯誤頁面魏颓。比如:404 not found 就表示客戶端請求的資源找不到。
當瀏覽得到一個正確的 200 響應之后吱晒,接下來面臨的一個問題就是多國語言的編碼解析了甸饱,響應頭是一個 ascii 的標準字符集的文本,這個還好辦仑濒,但是響應的正文本質(zhì)上就是一個字節(jié)流叹话,對于這一坨字節(jié)流,瀏覽器要怎么去處理呢墩瞳?首先瀏覽器會去看響應頭里面指定的 encoding 域驼壶,如果有了這個東西,那么就按照指定的 encoding 去解析字符喉酌,如果沒有的話热凹,那么瀏覽器會使用一些比較智能的方式,去猜測和判斷這一坨字節(jié)流應該使用什么字符集去解碼泪电。相關的筆記可以看這里般妙,字符集編碼
接下來就是構(gòu)建 dom 樹了,在 html 語言嵌套正常而且規(guī)范的情況下相速,這種 xml 標記的語言是比較容易的能夠構(gòu)建出一棵 dom 樹出來的碟渺,當然,對于互聯(lián)網(wǎng)上大量的不規(guī)范的頁面突诬,不同的瀏覽器應該有自己不同的容錯去處理苫拍。構(gòu)建出來的 dom 本質(zhì)上還是一棵抽象的邏輯樹,構(gòu)建 dom 樹的過程中旺隙,如果遇到了由 script 標簽包起來的 js 動態(tài)腳本代碼绒极,那么會把代碼送到 js 引擎里面去跑,如果遇到了 style 標簽包圍起來的 css 代碼催束,也會保存下來集峦,用于稍后的渲染。如果遇到了 img 或 css 和 js等引用外部文件的標簽,那么瀏覽器會根據(jù)指定的 url 再次發(fā)起一個新的 http 請求塔淤,去把這個文件拉取回來摘昌,值得一提的是,對于同一個域名下的下載過程來說高蜂,瀏覽器一般允許的并發(fā)請求是有限的聪黎,通常控制在兩個左右备恤,所以如果有很多的圖片的話稿饰,一般出于優(yōu)化的目的,都會把這些圖片使用一臺靜態(tài)文件的服務器來保存起來露泊,負責響應喉镰,從而減少主服務器的壓力。
dom 樹構(gòu)造好了之后惭笑,就是根據(jù) dom 樹和 css 樣式表來構(gòu)造 render 樹了侣姆,這個才是真正的用于渲染到頁面上的一個一個的矩形框的樹,網(wǎng)頁渲染是瀏覽器最復雜沉噩、最核心的功能捺宗,對于 render 樹上每一個框,需要確定他的 x y 坐標川蒙,尺寸蚜厉,邊框,字體畜眨,形態(tài)昼牛,等等諸多方面的東西,render 樹一旦構(gòu)建完成胶果,整個頁面也就準備好了匾嘱,可以上菜了斤斧。需要說明的是早抠,下載頁面,構(gòu)建 dom 樹撬讽,構(gòu)建 render 樹這三個步驟蕊连,實際上并不是嚴格的先后順序的,為了加快速度游昼,提高效率甘苍,讓用戶不要等那么久,現(xiàn)在一般都并行的往前推進的烘豌,現(xiàn)代的瀏覽器都是一邊下載载庭,下載到了一點數(shù)據(jù)就開始構(gòu)建 dom 樹,也一邊開始構(gòu)建 render 樹,構(gòu)建了一點就顯示一點出來囚聚,這樣用戶看起來就不用等待那么久了靖榕。
10)將渲染好的頁面圖像顯示出來,并開始響應用戶的操作顽铸。
這一步主要涉及顯卡茁计,內(nèi)存及顯示器原理等知識,不做詳細解說谓松,大概就是從內(nèi)存到 LCD/LED星压,再由光線進入人眼的一個過程。
以上過程簡單講主要是:從輸入 URL 到瀏覽器接收(回車前)鬼譬,從瀏覽器接收到數(shù)據(jù)如何發(fā)送給網(wǎng)卡(回車后)娜膘,再把接收的數(shù)據(jù)從本機網(wǎng)卡發(fā)送到服務器,服務器接收到數(shù)據(jù)后做了怎么的處理优质?服務器返回數(shù)據(jù)后瀏覽器又做了哪些處理劲绪?瀏覽器又是如何將處理好的頁面展現(xiàn)在屏幕上的?的這么一個過程盆赤。
但只是最基本的一些步驟贾富,實際不可能就這么簡單,一些可選的步驟例如網(wǎng)頁緩存牺六、連接池颤枪、加載策略、加密解密淑际、代理中轉(zhuǎn)等等都沒有提及畏纲。即使基本步驟本身也有很復雜的子步驟,TCP/IP春缕、DNS盗胀、HTTP、HTML等等锄贼,還需要考慮很多情況票灰,比如廣播、拆包解包合并包丟包重傳宅荤、路由表屑迂,NAT、TCP 狀態(tài)機冯键、CDN惹盼、HTTPS 證書校驗與中間人攻擊檢測、RSA 密鑰協(xié)商惫确、AES 加解密手报、瀏覽器解析 HTTP 的有限自動狀態(tài)機蚯舱、GUI 庫與繪圖、OpenGL 繪圖掩蛤、GPU 加速(OpenCL 與 CUDA)晓淀、JIT(JavaScript 會把 JavaScript 代碼編譯成匯編代碼)、服務器的數(shù)據(jù)庫 NoSQL 或 SQL 查詢盏档、主從數(shù)據(jù)庫同步凶掰、服務器和瀏覽器的內(nèi)存管理(WebKit 實現(xiàn)的 fastMalloc(),服務器上可能是 TCMalloc 或者 JeMalloc)蜈亩、服務器上的語言解釋器(可能也是 JIT)懦窘、多媒體:傅里葉變換、H.264 解碼(硬件解碼稚配,硬件解碼的話 GPU 的處理單元又在計算.......或軟件解碼)畅涂、音頻解碼、WebGL 繪圖道川、瀏覽器的 Sandbox、服務器的 SQL 注入檢查、產(chǎn)生的鍵盤中斷信號處理(或者是高級層面的輸入輸出驅(qū)動)帅戒、網(wǎng)卡驅(qū)動逻住、網(wǎng)絡棧的 TCP FastOpen瞎访、SYN Cookie 之類眾多技術……每一個都可以展開成龐大的課題,而瀏覽器的基礎——操作系統(tǒng)鸦采、編譯器、硬件等更是一個比一個復雜肄程。即便是計算機專業(yè)的同學看了也會頭大蓝厌,但我保證這里面的每一個步驟都經(jīng)過深思熟慮和時間的考驗的,并不是誰閑的蛋疼非要搞得那么復雜读恃,不復雜也不行啊寺惫。你輸入URL即可瀏覽互聯(lián)網(wǎng)西雀,而計算機系統(tǒng)在背后做了無數(shù)你看不到的工作艇肴,計算機各個子領域無數(shù)工程師為此付出了你難以想象的努力再悼。有興趣的可以閱讀下
你剛才在淘寶上買了一件東西
http://coolshell.cn/articles/9666.html
http://www.reibang.com/p/e305ace24ddf
https://segmentfault.com/a/1190000005169412
https://www.chengrang.com/how-browsers-work.html
參考資料:
了解html頁面的渲染過程
前端工程師手冊
瀏覽器渲染那些事
手機上從輸入URL到頁面加載完成的過程中都發(fā)生了什么?
當頁面渲染時,瀏覽器發(fā)生了什么憾筏?
瀏覽器工作原理分析與首屏加載
https://www.youtube.com/watch?v=eeS4brbDVuU