一次完整的http請求過程


寫在前面php


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

請求處理過程:web

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

 

域名解析數據庫


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

解析順序:後端

檢查瀏覽器自身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服務器會在日誌文件中添加一個條目,來描述已執行的事務

相關文章
相關標籤/搜索