剛開始寫這篇文章還是挺糾結的,因為網上搜尋“從輸入url到頁面展示到底發生了什麼”,你可以搜到一大堆的資料。而且面試這題基本上是必考題,二月面試的時候,雖然知道這個過程發生了什麼,不過當面試官一步步追問下去的,很多細節就不太清楚了。
本文的目的是透過輸入url之後發生的事情來做知識的總結和擴展。所以文章可能會很雜。
總的過程大概如下:
當我們開始在瀏覽器中輸入網址的時候,瀏覽器其實就已經在智能的匹配可能得url 了,他會從歷史記錄,書籤等地方,找到已經輸入的字符串可能對應的url,然後給出智能提示,讓你可以補全url地址。對於 google的chrome 的瀏覽器,他甚至會直接從快取中把網頁展示出來,就是說,你還沒按下 enter,頁面就出來了。
1、請求一旦發起,瀏覽器首先要做的事情就是解析這個域名,一般來說,瀏覽器會先查看本地硬碟的hosts 文件,看看其中有沒有和這個網域對應的規則,如果有的話就直接使用hosts 文件裡面的ip 位址。
2、如果在本地的 hosts 檔案沒有能夠找到對應的 ip 位址,瀏覽器會發出一個 DNS請求到本地DNS伺服器 。本地DNS伺服器一般都是你的網路存取伺服器商提供,例如中國電信,中國移動。
3、查詢你輸入的網址的DNS請求到達本地DNS伺服器之後,本地DNS伺服器會先查詢它的快取記錄,如果快取中有此筆記錄,就可以直接傳回結果,此過程是遞歸的方式進行查詢。如果沒有,本機DNS伺服器也要向DNS根伺服器進行查詢。
4、根DNS伺服器沒有記錄特定的網域名稱和IP位址的對應關係,而是告訴本地DNS伺服器,你可以到網域伺服器上去繼續查詢,並給予網域伺服器的地址。這種過程是迭代的過程。
5、本地DNS伺服器繼續向網域伺服器發出請求,在這個範例中,請求的物件是.com網域伺服器。 .com網域伺服器收到請求之後,也不會直接回傳網域名稱和IP位址的對應關係,而是告訴本地DNS伺服器,你的網域的解析伺服器的位址。
6、最後,本地DNS伺服器向網域名稱的解析伺服器發出請求,這時就能收到一個網域名稱和IP位址對應關係,本地DNS伺服器不只要把IP位址回傳給用戶電腦,還要把這個對應關係保存在快取中,以便下次別的使用者查詢時,可以直接回傳結果,加快網路存取。
下面這張圖很完美的解釋了這個過程:
DNS(Domain Name System,網域名稱系統),因特網路上作為網域名稱和IP位址相互映射的一個分散式資料庫,能夠使用戶更方便的存取互聯網,而不用去記住能夠被機器直接讀取的IP數串。透過主機名,最後得到該主機名對應的IP位址的過程叫做網域解析(或稱主機名解析)。
通俗的講,我們更習慣於記住一個網站的名字,例如www.baidu.com,而不是記住它的ip位址,例如:167.23.10.2。而電腦更擅長記住網站的ip位址,而不是像www.baidu.com等連結。因為,DNS就相當於一個電話本,例如你要找www.baidu.com這個域名,那我翻一翻我的電話本,我就知道,哦,它的電話(ip)是167.23.10.2。
1、遞迴解析
當局部DNS伺服器本身無法回答客戶機的DNS查詢時,它就需要向其他DNS伺服器進行查詢。此時有兩種方式,如圖所示的是遞歸方式。局部DNS伺服器自己負責向其他DNS伺服器查詢,一般是先向該網域的根網域伺服器查詢,再由根網域伺服器一級向下查詢。最後得到的查詢結果回傳給局部DNS伺服器,再由局部DNS伺服器傳回給客戶端。
2、迭代解析
當局部DNS伺服器本身無法回答客戶機的DNS查詢時,也可以透過迭代查詢的方式解析,如圖所示。局部DNS伺服器不是自己向其他DNS伺服器進行查詢,而是把能解析該網域的其他DNS伺服器的IP位址傳回給客戶端DNS程序,客戶端DNS程序再繼續向這些DNS伺服器進行查詢,直到得到查詢結果為止。也就是說,迭代解析只是幫你找相關的伺服器而已,而不會幫你查。比如說:baidu.com的伺服器ip位址在192.168.4.5這裡,你自己去查吧,本人比較忙,只能幫你到這裡了。
我們在前面有說到根DNS伺服器,域DNS伺服器,這些都是DNS域名稱空間的組織方式。依其功能命名空間中用來描述DNS 域名稱的五個類別的介紹詳見下表中,以及與每個名稱類型的範例
(盜圖)
當一個網站有足夠多的用戶的時候,假如每次請求的資源都位於同一台機器上面,那麼這台機器隨時可能會彈跳。處理辦法就是用DNS負載平衡技術,它的原理是在DNS伺服器中為同一個主機名稱配置多個IP位址,在應答DNS查詢時,DNS伺服器對每個查詢將以DNS檔案中主機記錄的IP位址依序返回不同的解析結果,將客戶端的存取引導到不同的機器上去,使得不同的客戶端訪問不同的伺服器,從而達到負載平衡的目的。例如可以根據每台機器的負載量,該機器離用戶地理位置的距離等等。
拿到網域對應的IP位址之後,瀏覽器會以一個隨機連接埠(1024<連接埠< ;65535)向伺服器的WEB程式(常用的有httpd,nginx等)80埠發起TCP的連線請求<span style="font-size: 16px;">。 </span>
這個連接請求到達伺服器端後(這中間透過各種路由設備,區域網路內除外),進入到網卡,然後是進入到核心的TCP/IP協定棧(用於識別該連接請求,解開數據包,一層一層的剝開),還有可能要經過Netfilter防火牆(屬於內核的模組)的過濾,最終到達WEB程序,最終建立了TCP/IP的連接。
TCP連線如圖所示:
#建立了TCP連線之後,發起一個http請求。一個典型的 http request header 一般需要包含請求的方法,例如 GET 或 POST 等,不常用的還有 PUT 和 DELETE 、HEAD、OPTION以及 TRACE 方法,一般的瀏覽器只能發起 GET 或 POST 請求。
客戶端向伺服器發起http請求的時候,會有一些請求訊息,請求訊息包含三個部分:
| 請求方法URI協定/版本
| 請求標頭(Request Header)
| 請求文字:
## | 請求文字:
#下面是一個完整的HTTP請求範例:GET/sample.jspHTTP/1.1Accept:image/gif.image/jpeg,*/*Accept-Language:zh-cn Connection:Keep-Alive Host:localhost User-Agent:Mozila/4.0(compatible;MSIE5.01;Window NT5.0) Accept-Encoding:gzip,deflate username=jinqiao&password=1234
注意:
最後一個請求頭之後是一個空行,發送回車符和換行符,通知伺服器以下不再有請求頭。
請求頭包含許多相關的客戶端環境和請求正文的有用資訊。例如,請求頭可以聲明瀏覽器所使用的語言,請求正文的長度等。
Accept:image/gif.image/jpeg.*/* Accept-Language:zh-cn Connection:Keep-Alive Host:localhost User-Agent:Mozila/4.0(compatible:MSIE5.01:Windows NT5.0) Accept-Encoding:gzip,deflate.
username=jinqiao&password=1234
伺服器給予瀏覽器回應301永久重新導向回應,這樣瀏覽器就會造訪「http://www.google.com/」 而非「http://google.com/」。
###### 為什麼伺服器一定要重定向而不是直接傳送使用者想看的網頁內容呢?其中一個原因跟搜尋引擎排名有關。如果一個頁面有兩個位址,就像http://www.yy.com/和http://yy.com/,搜尋引擎會認為它們是兩個網站,結果造成每個搜尋連結都減少從而降低排名。而搜尋引擎知道301永久重定向是什麼意思,這樣就會把訪問帶www的和不帶www的地址歸到同一個網站排名下。還有就是用不同的地址會造成快取友善變差,當一個頁面有好幾個名字時,它可能會在快取裡出現好幾次。 ######301和302狀態碼都表示重定向,就是說瀏覽器在拿到伺服器回傳的這個狀態碼後會自動跳到一個新的URL位址,這個位址可以從回應的Location首部取得(使用者看到的效果就是他輸入的地址A瞬間變成了另一個地址B)-這是它們的共同點。
他們的不同在於。 301表示舊位址A的資源已經被永久地移除了(這個資源不可訪問了),搜尋引擎在抓取新內容的同時也將舊的網址交換為重定向之後的網址;
302表示舊位址A的資源還在(仍然可以存取),這個重定向只是暫時地從舊位址A跳到位址B,搜尋引擎會抓取新的內容而儲存舊的網址。 SEO302優於301
2)重定向原因:
(1)網站調整(如改變網頁目錄結構);
(2)網頁移到一個新位址;
(3)網頁副檔名改變(如應用程式需要把.php改成.Html或.shtml)。
這種情況下,如果不做重定向,則用戶收藏夾或搜尋引擎資料庫中舊地址只能讓訪問客戶得到一個404頁面錯誤訊息,訪問流量白白喪失;再者某些註冊了多個網域的網站,也需要透過重新導向讓造訪這些網域的使用者自動跳到主網站等。
當網站或網頁24—48小時內暫時移動到新的位置,這時候就要進行302跳轉,而使用301跳轉的場景就是因為某種原因需要移除掉,然後要到新的地址訪問,是永久性的。
清楚明確而言:使用301跳轉的大概場景如下:
1、網域到期不想續約(或發現了更適合網站的網域),想換個網域。
2、在搜尋引擎的搜尋結果中出現了沒有www的域名,而帶www的域名卻沒有收錄,這個時候可以用301重定向來告訴搜尋引擎我們目標的域名是哪一個。
3、空間伺服器不穩定,換空間的時候。
現在瀏覽器知道了"http://www.google.com/"才是要存取的正確位址,所以它會發送另一個http請求。這裡沒有啥好說的
經過前面的重重步驟,我們終於將我們的http請求發送到了伺服器這裡,其實前面的重定向已經是到達伺服器了,那麼,伺服器是如何處理我們的請求的呢?
后端从在固定的端口接收到TCP报文开始,它会对TCP连接进行处理,对HTTP协议进行解析,并按照报文格式进一步封装成HTTP Request对象,供上层使用。
一些大一点的网站会将你的请求到反向代理服务器中,因为当网站访问量非常大,网站越来越慢,一台服务器已经不够用了。于是将同一个应用部署在多台服务器上,将大量用户的请求分配给多台机器处理。此时,客户端不是直接通过HTTP协议访问某网站应用服务器,而是先请求到Nginx,Nginx再请求应用服务器,然后将结果返回给客户端,这里Nginx的作用是反向代理服务器。同时也带来了一个好处,其中一台服务器万一挂了,只要还有其他服务器正常运行,就不会影响用户使用。
如图所示:
通过Nginx的反向代理,我们到达了web服务器,服务端脚本处理我们的请求,访问我们的数据库,获取需要获取的内容等等,当然,这个过程涉及很多后端脚本的复杂操作。由于对这一块不熟,所以这一块只能介绍这么多了。
经过前面的6个步骤,服务器收到了我们的请求,也处理我们的请求,到这一步,它会把它的处理结果返回,也就是返回一个HTPP响应。
HTTP响应与HTTP请求相似,HTTP响应也由3个部分构成,分别是:
l 状态行
l 响应头(Response Header)
l 响应正文
HTTP/1.1 200 OK Date: Sat, 31 Dec 2005 23:59:59 GMT Content-Type: text/html;charset=ISO-8859-1 Content-Length: 122<html> <head> <title>http</title> </head><body> <!-- body goes here --> </body> </html>
状态行:
状态行由协议版本、数字形式的状态代码、及相应的状态描述,各元素之间以空格分隔。
格式: HTTP-Version Status-Code Reason-Phrase CRLF
例如: HTTP/1.1 200 OK
-- 協定版本:是用http1.0或其他版本
-- 狀態描述:狀態描述給出了關於狀態代碼的簡短的文字描述。例如狀態碼200時的描述為ok
-- 狀態碼:狀態碼由三位數字組成,第一個數字定義了回應的類別,且有五種可能取值。如下
1xx:資訊性狀態碼,表示伺服器已接收了客戶端請求,客戶端可繼續傳送請求。
100 Continue
# 101 Switching Protocols
# 2xx:成功狀態碼,表示伺服器已成功接收到請求並進行處理。
200 OK 表示客戶端請求成功
204 No Content 成功,但不回傳任何實體的主體部分
206 Partial Content 成功執行了一個範圍(Range)請求
3xx:重定向狀態碼,表示伺服器要求客戶端重定向。
301 Moved Permanently 永久重定向,回應封包的Location首部應該有此資源的新URL
302 Found 暫時性重定向,回應封包的Location首部給予的URL用來臨時定位資源
303 See Other 請求的資源存在另一個URI,客戶端應使用GET方法定向取得請求的資源
304 Not Modified 伺服器內容沒有更新,直接讀取瀏覽器快取
307 Temporary Redirect 暫時重定向。與302 Found含義一樣。 302禁止POST轉換為GET,但實際使用時並不一定,307則更多瀏覽器可能會遵循此標準,但也依賴瀏覽器具體實作
4xx:客戶端錯誤狀態碼,表示客戶端的請求有非法內容。
400 Bad Request 表示客戶端請求有語法錯誤,不能被伺服器所理解
401 Unauthonzed 表示請求未經授權,該狀態碼必須與WWW-Authenticate標頭域一起使用
403 Forbidden 表示伺服器收到請求,但拒絕提供服務,通常會在回應內文中給予不提供服務的原因
# 404 Not Found 請求的資源不存在,例如,輸入了錯誤的URL
5xx:伺服器錯誤狀態碼,表示伺服器未能正常處理客戶端的請求而出現意外錯誤。
500 Internel Server Error 表示伺服器發生無法預期的錯誤,導致無法完成客戶端的請求
503 Service Unavailable 表示目前伺服器無法處理客戶端的請求,在一段時間之後,伺服器可能會恢復正常
回應頭:
回應頭部:由關鍵字/值對組成,每行一對,關鍵字和值用英文冒號":"分隔,典型的回應頭有:
回應正文
包含我們需要的一些具體訊息,例如cookie,html,image,後端傳回的請求資料等等。這裡要注意,回應正文和回應頭之間有一行空格,表示回應頭的資訊到空格為止,下圖是fiddler抓到的請求正文,紅色框中的:回應正文:
在瀏覽器沒有完整接受全部HTML文件時,它就已經開始顯示這個頁面了,瀏覽器是如何把頁面呈現在螢幕上的呢?不同瀏覽器可能解析的過程不太一樣,這裡我們只介紹webkit的渲染過程,下圖對應的就是WebKit渲染的過程,這個過程包含:
解析html以建立dom樹-> 建立render樹-> 佈局render樹-> 繪製render樹
瀏覽器在解析html檔案時,會」自上而下「加載,並在加載過程中進行解析渲染。在解析過程中,如果遇到請求外部資源時,例如圖片、外鏈的CSS、iconfont等,請求過程是非同步的,並不會影響html文檔進行載入。
解析過程中,瀏覽器首先會解析HTML檔案建構DOM樹,然後解析CSS檔案建構渲染樹,等到渲染樹建置完成後,瀏覽器開始佈局渲染樹並將其繪製到螢幕上。這個過程比較複雜,涉及到兩個概念: reflow(回流)和repain(重繪)。
DOM節點中的各個元素都是以盒模型的形式存在,這些都需要瀏覽器去計算其位置和大小等,這個過程稱為relow;當盒模型的位置,大小以及其他屬性,如顏色,字體,等確定下來之後,瀏覽器便開始繪製內容,這個過程稱為repain。
頁面在首次載入時必然會經歷reflow和repain。 reflow和repain過程是非常消耗效能的,尤其是在行動裝置上,它會破壞使用者體驗,有時會造成頁面卡頓。所以我們應該盡可能少的減少reflow和repain。
# 當文件載入過程中遇到js文件,html文檔會掛起渲染(載入解析渲染同步)的線程,不僅要等待文檔中js檔案載入完畢,還要等待解析執行完畢,才可以恢復html文檔的渲染線程。因為JS有可能會修改DOM,最經典的document.write,這意味著,在JS執行完成前,後續所有資源的下載可能是沒有必要的,這是js阻塞後續資源下載的根本原因。所以我明平常的程式碼中,js是放在html文檔最後的。
JS的解析是由瀏覽器中的JS解析引擎完成的,例如Google的是V8。 JS是單執行緒運行,也就是說,在同一時間只能做一件事,所有的任務都需要排隊,前一個任務結束,後一個任務才能開始。但是又存在某些任務比較耗時,如IO讀寫等,所以需要一種機制可以先執行排在後面的任務,這就是:同步任務(synchronous)和異步任務(asynchronous)。
JS的執行機制就可以看做是一個主執行緒加上一個任務佇列(task queue)。同步任務就是放在主執行緒上執行的任務,非同步任務是放在任務佇列中的任務。所有的同步任務在主執行緒上執行,形成一個執行棧;非同步任務有了運行結果就會在任務隊列中放置一個事件;腳本運行時先依次運行執行棧,然後會從任務隊列中提取事件,運行任務佇列中的任務,這個過程是不斷重複的,所以又叫做事件迴圈(Event loop)。具體的過程可以看我這篇文章:點這裡
其實這個步驟可以並列在步驟8中,在瀏覽器顯示HTML時,它會注意到需要取得其他位址內容的標籤。這時,瀏覽器會發送一個獲取請求來重新獲得這些文件。例如我要取得外圖片,CSS,JS檔案等,類似下面的連結:
圖片:http://static.ak.fbcdn.net/rsrc.php/z12E0 /hash/8q2anwu7.gif
CSS樣式表:http://static.ak.fbcdn.net/rsrc.php/z448Z/hash/2plh8s4n.css
JavaScript 檔案:http://static.ak.fbcdn.net/rsrc.php/zEMOA/hash/c8yzb6ub.js
這些位址都要經歷一個和HTML讀取類似的過程。所以瀏覽器會在DNS中尋找這些域名,發送請求,重定向等等...
不像動態頁面,靜態檔案會允許瀏覽器對其進行快取。有的檔案可能不需要與伺服器通訊,而從快取直接讀取,或是可以放到CDN中
至此,從輸入url到頁面展示的過程終於整理完了。當然,文筆有限,有誤之處,歡迎指出,本文參考了很多的文章,不過很多文章的連結不記得了,所以只列出了下面三個參考連結。
以上是面試必問:從輸入url到頁面展示到底發生了什麼的詳細內容。更多資訊請關注PHP中文網其他相關文章!