一次完整的HTTP請(qǐng)求

這里講的請(qǐng)求是后端DevOps可以控制的范圍內(nèi),不包括DNS解析,層層的路由等等,一切都從請(qǐng)求到達(dá)我們自己架設(shè)的服務(wù)器開始。

1.與服務(wù)器建立連接

1.1 TCP連接的建立

客戶端的請(qǐng)求到達(dá)服務(wù)器,首先就是建立TCP連接

  1. Client首先發(fā)送一個(gè)連接試探,ACK=0 表示確認(rèn)號(hào)無效,SYN = 1 表示這是一個(gè)連接請(qǐng)求或連接接受報(bào)文,同時(shí)表示這個(gè)數(shù)據(jù)報(bào)不能攜帶數(shù)據(jù),seq = x 表示Client自己的初始序號(hào)(seq = 0 就代表這是第0號(hào)包),這時(shí)候Client進(jìn)入syn_sent狀態(tài),表示客戶端等待服務(wù)器的回復(fù)

  2. Server監(jiān)聽到連接請(qǐng)求報(bào)文后,如同意建立連接,則向Client發(fā)送確認(rèn)。TCP報(bào)文首部中的SYN 和 ACK都置1 ,ack = x + 1表示期望收到對(duì)方下一個(gè)報(bào)文段的第一個(gè)數(shù)據(jù)字節(jié)序號(hào)是x+1,同時(shí)表明x為止的所有數(shù)據(jù)都已正確收到(ack=1其實(shí)是ack=0+1,也就是期望客戶端的第1個(gè)包),seq = y 表示Server 自己的初始序號(hào)(seq=0就代表這是服務(wù)器這邊發(fā)出的第0號(hào)包)。這時(shí)服務(wù)器進(jìn)入syn_rcvd,表示服務(wù)器已經(jīng)收到Client的連接請(qǐng)求,等待client的確認(rèn)。

  3. Client收到確認(rèn)后還需再次發(fā)送確認(rèn),同時(shí)攜帶要發(fā)送給Server的數(shù)據(jù)。ACK 置1 表示確認(rèn)號(hào)ack= y + 1 有效(代表期望收到服務(wù)器的第1個(gè)包),Client自己的序號(hào)seq= x + 1(表示這就是我的第1個(gè)包,相對(duì)于第0個(gè)包來說的),一旦收到Client的確認(rèn)之后,這個(gè)TCP連接就進(jìn)入Established狀態(tài),就可以發(fā)起http請(qǐng)求了。

1.2 常見TCP連接限制

1.2.1 修改用戶進(jìn)程可打開文件數(shù)限制

在Linux平臺(tái)上,無論編寫客戶端程序還是服務(wù)端程序,在進(jìn)行高并發(fā)TCP連接處理時(shí),最高的并發(fā)數(shù)量都要受到系統(tǒng)對(duì)用戶單一進(jìn)程同時(shí)可打開文件數(shù)量的限制(這是因?yàn)橄到y(tǒng)為每個(gè)TCP連接都要?jiǎng)?chuàng)建一個(gè)socket句柄,每個(gè)socket句柄同時(shí)也是一個(gè)文件句柄)。可使用ulimit命令查看系統(tǒng)允許當(dāng)前用戶進(jìn)程打開的文件數(shù)限制,windows上是256,linux是1024,這個(gè)博客的服務(wù)器是65535

1.2.2 修改網(wǎng)絡(luò)內(nèi)核對(duì)TCP連接的有關(guān)限制

在Linux上編寫支持高并發(fā)TCP連接的客戶端通訊處理程序時(shí),有時(shí)會(huì)發(fā)現(xiàn)盡管已經(jīng)解除了系統(tǒng)對(duì)用戶同時(shí)打開文件數(shù)的限制,但仍會(huì)出現(xiàn)并發(fā)TCP連接數(shù)增加到一定數(shù)量時(shí),再也無法成功建立新的TCP連接的現(xiàn)象。出現(xiàn)這種現(xiàn)在的原因有多種。
第一種原因可能是因?yàn)長inux網(wǎng)絡(luò)內(nèi)核對(duì)本地端口號(hào)范圍有限制。此時(shí),進(jìn)一步分析為什么無法建立TCP連接,會(huì)發(fā)現(xiàn)問題出在connect()調(diào)用返回失敗,查看系統(tǒng)錯(cuò)誤提示消息是“Can’t assign requestedaddress”。同時(shí),如果在此時(shí)用tcpdump工具監(jiān)視網(wǎng)絡(luò),會(huì)發(fā)現(xiàn)根本沒有TCP連接時(shí)客戶端發(fā)SYN包的網(wǎng)絡(luò)流量。這些情況說明問題在于本地Linux系統(tǒng)內(nèi)核中有限制。

其實(shí),問題的根本原因在于Linux內(nèi)核的TCP/IP協(xié)議實(shí)現(xiàn)模塊對(duì)系統(tǒng)中所有的客戶端TCP連接對(duì)應(yīng)的本地端口號(hào)的范圍進(jìn)行了限制(例如,內(nèi)核限制本地端口號(hào)的范圍為1024~32768之間)。當(dāng)系統(tǒng)中某一時(shí)刻同時(shí)存在太多的TCP客戶端連接時(shí),由于每個(gè)TCP客戶端連接都要占用一個(gè)唯一的本地端口號(hào)(此端口號(hào)在系統(tǒng)的本地端口號(hào)范圍限制中),如果現(xiàn)有的TCP客戶端連接已將所有的本地端口號(hào)占滿,則此時(shí)就無法為新的TCP客戶端連接分配一個(gè)本地端口號(hào)了,因此系統(tǒng)會(huì)在這種情況下在connect()調(diào)用中返回失敗,并將錯(cuò)誤提示消息設(shè)為“Can’t assignrequested address”。

2.發(fā)起HTTP請(qǐng)求

2.1 請(qǐng)求格式

例如這樣的一個(gè)請(qǐng)求


Accept 就是告訴服務(wù)器端,我接受那些MIME類型

Accept-Encoding 這個(gè)看起來是接受那些壓縮方式的文件

Accept-Lanague 告訴服務(wù)器能夠發(fā)送哪些語言

Connection 告訴服務(wù)器支持keep-alive特性

Cookie 每次請(qǐng)求時(shí)都會(huì)攜帶上Cookie以方便服務(wù)器端識(shí)別是否是同一個(gè)客戶端

Host 用來標(biāo)識(shí)請(qǐng)求服務(wù)器上的那個(gè)虛擬主機(jī),比如Nginx里面可以定義很多個(gè)虛擬主機(jī)
那這里就是用來標(biāo)識(shí)要訪問那個(gè)虛擬主機(jī)。

User-Agent 用戶代理,一般情況是瀏覽器,也有其他類型,如:wget curl 搜索引擎的蜘蛛等

條件請(qǐng)求首部:
If-Modified-Since 是瀏覽器向服務(wù)器端詢問某個(gè)資源文件如果自從什么時(shí)間修改過,那么重新發(fā)給我,這樣就保證服務(wù)器端資源
文件更新時(shí),瀏覽器再次去請(qǐng)求,而不是使用緩存中的文件

安全請(qǐng)求首部:
Authorization: 客戶端提供給服務(wù)器的認(rèn)證信息

2.2 keep-alive/persitent

每次HTTP請(qǐng)求都重新建立TCP連接的開銷是很大的,于是就出現(xiàn)了keep-alive這個(gè)首部,它允許在一次TCP連接中發(fā)送/接收多個(gè)HTTP報(bào)文

</img>

然而,keep-alive是有弊端的。在HTTP1.0中,客戶端發(fā)起請(qǐng)求是加上keep-alive首部,服務(wù)端響應(yīng)時(shí)也加上keep-alive首部,那么這個(gè)請(qǐng)求就被認(rèn)為是keep-alive的,直到其中一方主動(dòng)斷開為止。如果沒有正確斷開,這個(gè)資源就會(huì)一直被占用了。

啞代理問題:啞代理只是單純的轉(zhuǎn)發(fā)請(qǐng)求,并不能進(jìn)行解析處理、維持持久連接等其他工作,而聰明的代理可以解析接收到的報(bào)文同時(shí)可以維持持久連接。

 如上圖,當(dāng)客戶端與服務(wù)器之間存在不解析直接轉(zhuǎn)發(fā)的代理時(shí),connection:keep-alive這個(gè)首部是直接轉(zhuǎn)發(fā)給服務(wù)器的,服務(wù)器接收了這個(gè)請(qǐng)求之后,就會(huì)向客戶端發(fā)送帶有connection:keep-alive的響應(yīng),同樣盲代理不會(huì)解析響應(yīng),直接將全部響應(yīng)轉(zhuǎn)發(fā)回客戶端。因?yàn)榭蛻舳耸盏搅诉@個(gè)首部,就認(rèn)為建立持久連接已經(jīng)成功了,但是中間的”笨代理“,并不知道這些事情,笨代理只有一種行為模式:在轉(zhuǎn)發(fā)請(qǐng)求和回送服務(wù)器響應(yīng)請(qǐng)求之后就認(rèn)為這次事務(wù)結(jié)束了,等待連接斷開,而這時(shí)由于connection:keep-alive首部已經(jīng)發(fā)送到服務(wù)器和客戶端,雙方都認(rèn)為持久連接已經(jīng)建立完成,這樣就變成了兩邊認(rèn)為持久連接OK而中間的啞代理等待連接斷開的情況,這種情況下如果客戶端再一次在這條連接上發(fā)送請(qǐng)求,請(qǐng)求就會(huì)在亞代理處停止,因?yàn)閱〈硪呀?jīng)在等待連接關(guān)閉。這種狀態(tài)會(huì)導(dǎo)致瀏覽器一直處于掛起狀態(tài),直到客戶端或服務(wù)器之中一個(gè)連接超時(shí),關(guān)閉連接為止,一段美好的牽手就這么沒了(啞代理就是把內(nèi)容原封不動(dòng)的轉(zhuǎn)發(fā)到代理)。

為了避免這種情況,現(xiàn)代的代理是不會(huì)轉(zhuǎn)發(fā)connection:keep-alive這個(gè)首部的。

persistent

HTTP/1.1的持久連接默認(rèn)是開啟的,只有首部中包含connection:close,才會(huì)事務(wù)結(jié)束之后關(guān)閉連接。當(dāng)然服務(wù)器和客戶端仍可以隨時(shí)關(guān)閉持久連接。

當(dāng)發(fā)送了connection:close首部之后客戶端就沒有辦法在那條連接上發(fā)送更多的請(qǐng)求了。當(dāng)然根據(jù)持久連接的特性,一定要傳輸正確的content-length。

還有根據(jù)HTTP/1.1的特性,是不應(yīng)該和HTTP/1.0客戶端建立持久連接的。最后,一定要做好重發(fā)的準(zhǔn)備。

管道化連接

HTTP/1.1允許在持久連接上使用管道,這樣就不用等待前一個(gè)請(qǐng)求的響應(yīng),直接在管道上發(fā)送第二個(gè)請(qǐng)求,在高延遲下,提高性能。

管道化連接的限制:

  • 不是持久連接就不能使用管道。
  • 必須按照同樣的發(fā)送順序回送響應(yīng),因?yàn)閳?bào)文沒有標(biāo)簽,很可能就順序就亂咯。
  • 因?yàn)榭梢噪S時(shí)關(guān)閉持久連接,所以要隨時(shí)做好重發(fā)準(zhǔn)備
  • 不應(yīng)該使用管道化發(fā)送重復(fù)發(fā)送會(huì)有副作用的請(qǐng)求(如post,重復(fù)提交)。

3.負(fù)載均衡

接收到HTTP請(qǐng)求之后,就輪到負(fù)載均衡登場了,它位于網(wǎng)站的最前端,把短時(shí)間內(nèi)較高的訪問量分?jǐn)偟讲煌瑱C(jī)器上處理。負(fù)載均衡方案有軟件、硬件兩種

F5 BIG-IP是著名的硬件方案,但這里不作討論

軟件方案有LVS HAProxy Nginx等,留作以后補(bǔ)充

4.Nginx(WEB服務(wù)器)

在典型的Rails應(yīng)用部署方案中,Nginx的作用有兩個(gè)

  1. 處理靜態(tài)文件請(qǐng)求
  2. 轉(zhuǎn)發(fā)請(qǐng)求給后端的Rails應(yīng)用

這是一個(gè)簡單的Nginx配置文件

</img>

后端的Rails服務(wù)器通過unix socket與Nginx通信,Nginx伺服public文件夾里的靜態(tài)文件給用戶

5.Rails(應(yīng)用服務(wù)器)

這篇文章無敵了,我沒有更多可以寫的,只能說一句我用的是Puma。因?yàn)榉?wù)器是單核的,只能用多線程Puma或者事件驅(qū)動(dòng)的Thin,考慮到以后可能用上Rails 5 ActionCabel,還是直接上Puma吧。

6.數(shù)據(jù)庫(數(shù)據(jù)庫服務(wù)器)

應(yīng)用服務(wù)器想訪問數(shù)據(jù)庫,就需要與數(shù)據(jù)庫建立連接。Rails讀取database.yml中的配置,訪問對(duì)應(yīng)的數(shù)據(jù)庫。

一個(gè)重要的配置指標(biāo)pool: Rails中的數(shù)據(jù)庫連接是完全線程安全的,所有pool的值要配置成與Puma的最大線程數(shù)相等,這樣就不會(huì)出現(xiàn)線程等待數(shù)據(jù)庫連接的情況。

7.Redis、Memercache(緩存服務(wù)器)

8.消息隊(duì)列

9.搜索

參考文獻(xiàn)

一次完整的HTTP事務(wù)是怎樣一個(gè)過程?

Linux下高并發(fā)socket最大連接數(shù)所受的各種限制

Keep-Alive

談?wù)劤志眠B接——HTTP權(quán)威指南讀書心得(五)

數(shù)據(jù)庫連接池的工作原理

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容