一次完整的http請求過程


寫在前面


分析http的請求處理過程能夠幫助讀者更加深入的理解web服務架構的理解,爲以後提升打下良好的基礎,以下過程爲本人在學習完lamp架構後的總結,如有錯誤,敬請指正。wKioL1nkBtegqRdtAAD1yuQt-S8463.png

請求處理過程:

域名解析--> 建立連接--> 接收請求--> 處理請求 --> 訪問資源 --> 構建響應報文--> 發送響應報文  --> 記錄日誌

 

域名解析


假設用戶在瀏覽器地址欄中輸入http://yangzhiheng.blog.51cto.com發起一個請求。首先會把該域名(準確叫法爲完全限定域名或者主機名)解析爲ip地址。那麼如何解析呢?

解析順序:

檢查瀏覽器自身DNS緩存—>  操作系統DNS緩存 –>hosts文件–>DNS解析

  1. 檢查瀏覽器自身的DNS緩存,看自身緩存中是否有該FQDN的對應條目如果沒有過期,則解析到所對應的ip地址。若沒有該條目或者條目已過期則執行第2步。谷歌瀏覽器中查看緩存具體細節可在地址欄中輸入chrome://net-internals/#dns來查看

  2.  檢查操作系統自身所對應的DNS緩存是否有所對應的條目,對於linux主機,系統默認不對DNS進行緩存。如果在操作系統的dns緩存中找到未過期的所對應的條目,則解析到此結束。否則執行下一步。Windows系統查看系統dns緩存可以使用ipconfig /displaydns命令。

  3.  檢查hosts文件中是否有該FQDN所對應的條目,如果有則解析成功,沒有則進行下一步。在Windows系統中hosts文件位於C:\Windows\Sytem32\drivers\etc\目錄下。linux系統中位於/etc/hosts目錄下。

  4. 如果hosts文件中也不存在該域名所對應的條目,瀏覽器會向本地的DNS服務器發起一個域名解析請求。域名解析請求使用udp的53端口,本地DNS請求爲遞歸請求。本地的DNS服務器會先檢查自身的緩存,是否有該FQDN所對應的條目。如果有切且沒有過期則返回給瀏覽器,解析成功。如果沒有找到對應條目則本地DNS服務器發起一個迭代的DNS解析請求,首先向根域名服務器查找,根域名服務器在全球有13個。如果根域名服務器中存儲的是頂級域名所對應的DNS服務器的ip地址。以yangzhiheng.blog.51cto.com爲例,根域名返回給本地DNS服務器一個com域所對應的DNS服務器地址,本地DNS服務器向com域名服務器請求該FQDN請求,com域名服務器則返回給本地服務器一個51cto域的DNS服務器,這樣層層迭代,最終返回給本地DNS一個正確的ip地址。本地服務器得到ip地址後緩存下來,並返回給瀏覽器。瀏覽器根據該ip地址來進行訪問。

DNS 的詳細解析過程,讀者可以參考http://vinsent.blog.51cto.com/13116656/1967876該文章來加深理解。


建立連接


得到IP地址後,瀏覽器會開啓一個隨機端口向web服務器的80端口發起tcp鏈接請求,經過3次握手後建立tcp連接,然後瀏覽器發起httpd請求。

建立連接過程:

三次握手  --> 發起http請求

三次握手過程詳解

 

wKiom1nkCW-C7CjrAADns2zTVnQ666.png

  1.  假定客戶端A爲有一個瀏覽器(TCP客戶端程序),服務器B運行httpd服務程序(TCP服務器程序)。最初兩端的tcp進程都處於一種closed狀態。客戶端A爲主動打開連接(開啓隨機端口,並創建傳輸控制塊),服務器B被動打開連接(啓動httpd服務,使80端口爲監聽狀態,準備接受客戶端進程的連接請求)。

  2. 服務器B收到連接請求報文後,如果同意與客戶端A建立連接則向A發送確認。在確認報文中SYNACK位都置1,確認號ack = x+1,同時也爲自己選擇一個初始序號seq=y。該報文也不攜帶數據,但也要消耗一個序號。這時服務B進入SYN-RCVD(同步收到)狀態。表示服務器B已經收到客戶端A的連接請求,等待客戶端A的確認。

  3. 客戶端進程收到B的確認後,還要向B給出確認。確認報文中的ACK1,確認號ack=y+1,表示期望收到第y+1個報文。自己的報文序號seq=x+1TCP協議規定該ACK的確認包可以攜帶,但是如果不攜帶數據就不消耗序列號。也就是說這個報文發出去後,如果不攜帶數據則下一個報文發送該序列號仍是seq=x+1。這時tcp連接以建立。客戶端進入ESTABLISHED狀態。當服務器B收到該確認報文後,也進入ESTABLISHED狀態。

TCP爲什麼要進行三次握手?

  在日常生活中,連接的建立只需兩次握手應該就可以了,客戶端發送一個請求,服務器端發送一個確認報文就可以建立連接了。但是爲什麼客戶端A爲什麼還要再一次發送確認報文呢?主要是爲了防止已失效的連接請求報文段突然又傳送到了服務器B,因而產生錯誤。

 已失效的連接請求報文段是這樣產生的,比如客戶端A發送了一個連接請求報文由於某些網絡原因在某個網絡節點滯留了,客戶端A誤以爲該數據包丟失,然後又發送一個連接請求報文,該報文正常到達後並數據傳輸完成後滯留的報文又傳送至服務器B。本來一個這就是一個早已失效的報文,但是B收到此報文後,就誤認爲客戶端A又發送了一次連接請求。假定不採用三次握手,服務器B發出確認後,新的連接就建立了,所以爲了防止上次情況的發生,就有了三次握手來建立連接。

 

發起http請求:

tcp建立連接之後,客戶端發起http請求。請求報文格式如下 

起始行:請求方法  請求的URL 請求的協議版本

頭部信息:user-agent  host等鍵值對

主體

無論請求報文還是響應報文都遵循該報文格式,其中請求方法一般有GET,POST,HEAD,PUT,DELETE,TRACE,OPTION等URL爲同一資源標識符,用於描述服務器某個特定資源的位置,組成:scheme://Server:Port/path/to/resource。協議版本嘗試用http/1.1版本。頭部信息一般爲host後面以冒號分隔要請求的主機名。

接收請求


  接收請求所要完成的工作就是接收來自網絡的請求報文中對某一資源的一次請求過程。其接收請求的模型包括以下幾類:

單進程I/O模型:啓動一個進程處理用戶請求,而且一次只處理一個,多個請求被串行響應。

多進程I/O結構:並行啓動多個進程,每個進程處理一個請求。

複用I/O結構:一個進程響應n個請求。

       多線程模型:一個進程生成n個線程,每個線程響應一個用戶請求。

       事件驅動:event-driven

                                  

複用的多線程I/O結構:啓動多個m進程,每個進程響應n個請求。

處理請求


  對請求報文進行分析,並獲取請求的資源及請求方法等相關信息。以Apacheprefork工作模式爲例,其管理進程在接收到請求報文後會選擇一個工作進程來對該請求進行處理,得到其請求的方法。和資源的URL等相關信息。

訪問資源


  在對請求處理時一般需要訪問後端資源,以lamp架構爲例,Apache工作進程把請求轉發到PHP-fpm管理進程(默認端口爲9000),PHP-FPM管理進程分配一個工作進程用於處理index.php請求,工作進程在服務器路徑中找到index.php文件,進行解析,編譯。然後執行編譯後的php代碼,這時一般還要訪問後端的數據庫服務器等。得到請求的結果,把結果返回給apache服務器。

構建響應報文


  Apache在得到返回的請求結果後,開始構建響應報文。響應報文中包含有響應狀態碼、響應首部,如果生成了響應主體的話,還包括響應主體。這時還有一個較爲重要的要點,就是資源訪問重定向問題。

web服務構建的響應並非客戶端請求的資源,而是資源的另外一個訪問路徑:可分爲永久重定向和臨時重定向。

永久重定向:curl -I http://www.360buy.com 得到的結果 HTTP/1.1 301 Moved Permanently 

臨時重定向:curl -I http://www.taobao.com 得到的結果  HTTP/1.1 302 Found

發送響應報文


響應報文構建完成之後,發送響應報文

記錄日誌


最後,當事務結束時,Web服務器會在日誌文件中添加一個條目,來描述已執行的事務


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章