當(dāng)前位置:
首頁 >
超高性能管线式HTTP请求(实践·原理·实现)
發(fā)布時(shí)間:2025/7/14
63
豆豆
生活随笔
收集整理的這篇文章主要介紹了
超高性能管线式HTTP请求(实践·原理·实现)
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
這里的高性能指的就是網(wǎng)卡有多快請(qǐng)求發(fā)送就能有多快,基本上一般的服務(wù)器在一臺(tái)客戶端的壓力下就會(huì)出現(xiàn)明顯延時(shí)。 該篇實(shí)際是介紹pipe管線的原理,下面主要通過其高性能的測(cè)試實(shí)踐,解析背后數(shù)據(jù)流量及原理。最后附帶一個(gè)簡(jiǎn)單的實(shí)現(xiàn) 實(shí)踐 先直接看對(duì)比測(cè)試方法 測(cè)試內(nèi)容單一客戶的使用盡可能快的方式向服務(wù)器發(fā)送一定量(10000條)請(qǐng)求,并接收返回?cái)?shù)據(jù) 對(duì)于單一客戶端對(duì)服務(wù)器進(jìn)行http請(qǐng)求,一般我們的方式 1:單進(jìn)程或線程輪詢請(qǐng)求(這個(gè)效能自然很低,原因會(huì)講到,也不用測(cè)試) 2:多條線程提前準(zhǔn)備數(shù)據(jù)等待信號(hào)(對(duì)客戶端性能要求較高) 3:提前準(zhǔn)備一組線程同時(shí)輪詢操作 4:使用系統(tǒng)/平臺(tái)自帶異步發(fā)送機(jī)制(實(shí)際就是平臺(tái)線程池的方式,發(fā)送與接收使用從線程池中的不同線程) 對(duì)于測(cè)試方案1,及方案2測(cè)試中性能較低沒有可比性,后面測(cè)試不會(huì)展示其結(jié)果 以下展示后面2種測(cè)試方法及當(dāng)前要說的管線式的方式
實(shí)現(xiàn) 如下為pipe的.NET簡(jiǎn)單實(shí)現(xiàn)類庫,及應(yīng)用該類庫的deom 測(cè)試工具 實(shí)現(xiàn)過程還是比較簡(jiǎn)單的可直接參看GitHub工程,MyPipeHttpHelper為實(shí)現(xiàn)pipe的工具類(代碼中有較詳細(xì)的注釋),PipeHttpRuner為使用該工具類編寫的測(cè)試工具
https://github.com/lulianqi/PipeHttp/?(工程地址) https://github.com/lulianqi/PipeHttp/tree/master/MyPipeHttpHelper?(類庫地址) https://github.com/lulianqi/PipeHttp/tree/master/PipeHttpRuner?(測(cè)試deom地址) 《新程序員》:云原生和全面數(shù)字化實(shí)踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀
- 先講管線式(pipe)測(cè)試方案(原理在后面會(huì)講到),測(cè)試中使用100條管線(管道),實(shí)際上更少甚至一條管線也是能達(dá)到近似的性能,不過多數(shù)服務(wù)器nginx限制一條管可以持續(xù)發(fā)送request的數(shù)量(大部分是100也有部分會(huì)是200或是更高),每條管線發(fā)送100個(gè)請(qǐng)求。
- 然后是線程組的方式準(zhǔn)備100條線程(100條線程并不是很多不會(huì)對(duì)系統(tǒng)本身有明顯影響),每條線程輪詢發(fā)送100個(gè)request。
- 異步方式的方式,10000全部提交發(fā)送線程,由線程池控制接收。
?
先解釋下截圖含義,后面的截圖也都是同樣的含義 第一副為任務(wù)管理器的截圖實(shí)線為接收數(shù)據(jù),虛線為發(fā)送數(shù)據(jù),取樣0.5s,每一個(gè)正方形的刻度為1.5s(因?yàn)槿蝿?wù)管理器繪圖策略速率上升太快過高的沒有辦法顯示,不過還是可以看到時(shí)間線) 第二副為資源管理器,添加了3個(gè)采樣器,紅色為CPU占用率,藍(lán)色為網(wǎng)絡(luò)接收速率,綠色為網(wǎng)絡(luò)發(fā)送速率。 測(cè)試中 一次原始請(qǐng)求大概130字節(jié),加上tcp,ip包頭,10000條大概也只有1.5Mb(包頭不會(huì)太多因?yàn)楣艿朗秸?qǐng)求里會(huì)有多個(gè)請(qǐng)求放到一個(gè)包里的情況,不過大部分服務(wù)器無法有這么快的響應(yīng)速度會(huì)有大量重傳的情況,實(shí)際上傳流量可能遠(yuǎn)大于理論值) 一次的回包大概在60Mb左右(因?yàn)闀?huì)有部分連接中途中斷所以不一定每次測(cè)試都會(huì)有10000個(gè)完整回復(fù)) 可以看到使用pipe形式性能表現(xiàn)非常突出,總體完成測(cè)試僅僅使用了5s左右 發(fā)送本身壓力比較小,可以看到0.5秒即到達(dá)峰值,其實(shí)這個(gè)時(shí)候基本10000條request已經(jīng)發(fā)送出去了,后面的流量主要來自于服務(wù)器端緩存等待(TCP window Full)來不及處理而照成是重傳,后面會(huì)講到。 再來看看response的接收,基本上也僅僅使用了0.5s即達(dá)到了接收峰值,使用大概5s 即完成了全部接收,因?yàn)闇y(cè)試中cpu占用上升并不明顯,而對(duì)于response的接收基本上是從tcp緩存區(qū)讀出后直接就存在了內(nèi)容里,也沒有涉及磁盤操作(所以基本上可以說對(duì)于pipe這個(gè)測(cè)試并沒有發(fā)揮出其全部性能,瓶頸主要在網(wǎng)絡(luò)帶寬上)。 再來看下線程組的方式(100條線程每條100次) 下面是異步接收的方式 很明顯的差距,對(duì)于線程組的形式大概使用了25秒,而異步接收使用了超過1分鐘的時(shí)間(異步接收的模式是平臺(tái)推薦的發(fā)送模式,正常應(yīng)用情況下性能是十分優(yōu)越的,而對(duì)于過高的壓力不如自定義的線程組,主要還是因?yàn)槠涫褂昧四J(rèn)的線程池,而默認(rèn)線程池不可能在短時(shí)間開100條線程出來用來接收數(shù)據(jù),所以大量的回復(fù)對(duì)線程池里的線程就會(huì)有大量的切換,通過設(shè)置默認(rèn)線程池?cái)?shù)量可以提高測(cè)試中的性能)。更為重要的是這2者中的無論哪一種方式在測(cè)試中,cpu的占用都幾乎是滿的(即是說為了完成測(cè)試計(jì)算機(jī)已經(jīng)滿負(fù)荷工作了,很難再有提高) 后面其實(shí)還針對(duì)jd,toabao,youku,包括公司自己的服務(wù)器進(jìn)行過測(cè)試,測(cè)試結(jié)果都是類似的,只要服務(wù)器不出問題基本上都有超過10倍的差距(如果客戶端帶寬足夠這個(gè)差距會(huì)更大)。 下面我們?cè)賹?duì)接口形式的HTTP進(jìn)行簡(jiǎn)單一次測(cè)試 這里選用網(wǎng)易電商的接口(電商的接口一般可承受的壓力比較大,這里前面已經(jīng)確認(rèn)測(cè)試不會(huì)對(duì)其正常使用造成實(shí)質(zhì)的影響) http://you.163.com/xhr/globalinfo/queryTop.json?__timestamp=1514784144074 (這里是一個(gè)獲取商品列表的接口) 測(cè)試數(shù)據(jù)設(shè)置如下?
?
?
請(qǐng)求量還是10000條接收的response數(shù)據(jù)大概有326Mb 30s之內(nèi)完成。基本上是網(wǎng)絡(luò)的極限,此時(shí)cpu也基本無然后壓力(100條管線,每條100個(gè)請(qǐng)求) 這里其實(shí)請(qǐng)求是帶時(shí)間戳的,因?yàn)闇y(cè)試時(shí)使用的是同一個(gè)時(shí)間戳,所以實(shí)際對(duì)應(yīng)用服務(wù)器的影響不大,真實(shí)測(cè)試時(shí)可以為每條請(qǐng)求設(shè)置不同時(shí)間戳(這里是因?yàn)橐菔臼褂昧司€上公開服務(wù),測(cè)試時(shí)請(qǐng)使用測(cè)試服務(wù)) 注意,這里的測(cè)試如果選擇了性能較低的測(cè)試對(duì)象,大部分流量會(huì)在服務(wù)器端排隊(duì)等候,導(dǎo)致吞吐量不大,這實(shí)際是服務(wù)器端處理過慢,與客戶端關(guān)系不大。 一般情況下一臺(tái)普通的pc在使用pipe進(jìn)行測(cè)試時(shí)就可以讓服務(wù)器出現(xiàn)明顯延時(shí) 原理 正常的http一般實(shí)現(xiàn)都是連接完成后(tcp握手)發(fā)生request流向服務(wù)器,然后及進(jìn)入等待,收到response后才算結(jié)束(如下圖)?
當(dāng)然http1.1 即支持keep alive,完成一次收發(fā)后完全可以不關(guān)閉連接使用同一個(gè)鏈接發(fā)生下一個(gè)請(qǐng)求(如下圖) 這種方式對(duì)性能的提升還是比較明顯的,特別早些年服務(wù)器性能有限,網(wǎng)絡(luò)資源匱乏,RTT大(網(wǎng)絡(luò)時(shí)延大)。不過對(duì)如今的情況,其實(shí)這些都已經(jīng)不是最主要的問題了 可以明顯看到上面的模式,是一定要等到response到達(dá)后,客戶端才能發(fā)起下一個(gè)request的,如果應(yīng)用服務(wù)器需要時(shí)間處理,所有后面的請(qǐng)求都需要等待,即使不需要任何處理直接回復(fù)給客戶端,請(qǐng)求,回復(fù)在網(wǎng)絡(luò)上的時(shí)間也是必須完整的等下去,而且由于tcp傳輸本身的特性,速率是逐步上升的,這樣斷斷續(xù)續(xù)的發(fā)送接收十分影響tcp迅速達(dá)到線路性能最大值。 pipe (管線式)正是回避了上面的問題,他不需要等回復(fù)達(dá)到即可直接發(fā)送(事實(shí)上http1.1協(xié)議也從來沒有講過必須要等response到達(dá)后客戶端才能發(fā)送下一個(gè)請(qǐng)求,只是為了方便應(yīng)用層業(yè)務(wù)實(shí)現(xiàn),一般的http庫都是這樣實(shí)現(xiàn)的,而現(xiàn)在看到的絕大多少http服務(wù)器都是默認(rèn)支持pipe的),這樣發(fā)送與接收即可以分離開來(如下圖)?
在事實(shí)情況下,發(fā)生可能會(huì)比這個(gè)圖表現(xiàn)的更快,請(qǐng)求1,2,3,4很可能被放到一個(gè)tcp包里被一次性全部發(fā)出去(這種模式也給部分應(yīng)用帶來了麻煩,后面會(huì)講到)?
對(duì)于pipe相對(duì)真實(shí)的情況如上圖,多個(gè)請(qǐng)求會(huì)被打包在一起被發(fā)送,甚至有時(shí)是所有request發(fā)送完成后,服務(wù)器才開始回復(fù)第一個(gè)response。?
而普通的keepalive的模式如上圖,一條線代表一個(gè)請(qǐng)求,不僅一次只能發(fā)送一個(gè),而且必須等待回復(fù)后才能發(fā)下一個(gè)。 下面看下實(shí)際測(cè)試中pipe的模式具體是什么模樣的?
可以看到握手完成后(實(shí)際上握手時(shí)間也不長只用了4ms),隨后即直接開始了request的發(fā)送,可以看到后面的一個(gè)tcp包里直接包含了完整的12個(gè)請(qǐng)求。在沒有收到任何一個(gè)回復(fù)的情況下,就可以把所有要發(fā)送的請(qǐng)求提前全部發(fā)出(服務(wù)器已經(jīng)關(guān)閉了Nagle算法)。?
由于發(fā)送速度過快直到發(fā)出一大半近70個(gè)request的時(shí)候第一個(gè)tcp確認(rèn)包序號(hào)為353的包(只是確認(rèn)包不是response)才發(fā)出(327的ack),而且服務(wù)器很快就發(fā)現(xiàn)下一個(gè)包出問題了并引發(fā)了TCP DUP ACK (https://ask.wireshark.org/questions/29216/why-are-duplicate-tcp-acks-being-seen-in-wireshark-capture 產(chǎn)生原因可以參考這里) 【TCP DUP ACK 出現(xiàn)在接收方發(fā)現(xiàn)數(shù)據(jù)包缺口時(shí)(數(shù)據(jù)包失序),這種情況就會(huì)發(fā)送重復(fù)的ACK,這不僅用于快重傳,會(huì)觸發(fā)比快重傳更快的恢復(fù)機(jī)制(Fast Retransmission)如果發(fā)現(xiàn)重復(fù)的ACK,但是報(bào)文中未發(fā)現(xiàn)缺口,這表示你捕獲的是數(shù)據(jù)來源(而不是接收方),這是十分正常的如果數(shù)據(jù)在發(fā)往接收方的時(shí)候發(fā)生了丟失。你應(yīng)該會(huì)看到一個(gè)重傳包】 其實(shí)就是說服務(wù)器沒有發(fā)現(xiàn)下一個(gè)包后面又發(fā)了3次(一共4次·)TCP DUP ACK 都是針對(duì)353的,所以后面客戶端很快就重傳了TCP DUP ACK 所指定的丟失的包(即下面看到的362) 后面還可以看到由于過快的速度,還造成了部分的失序列(out of order)。不過需要說明的是,這些錯(cuò)誤在tcp的傳輸中是很常見的,tcp有自己的一套高效的機(jī)制對(duì)這些錯(cuò)誤進(jìn)行恢復(fù),即便有這些錯(cuò)誤的存在也不會(huì)對(duì)pipe的實(shí)際性能造成影響。 如果服務(wù)器異常誤包不能馬上被恢復(fù)可能會(huì)造成指數(shù)退避的情況如下圖?
高速收發(fā)帶來的問題,不僅有丟包,失序,重傳,無論是客戶端還是服務(wù)器都會(huì)有接收窗口耗盡的情況,如果接收端窗口耗盡會(huì)出現(xiàn)TCP ZeroWIndow / Window full。 所以無論是客戶端還是服務(wù)器都需要快速讀取tcp緩沖區(qū)數(shù)據(jù)?
通過對(duì)TCP流的檢查可以確定在本次測(cè)試中的部分管道的100條request是全部發(fā)出后,response才逐步被服務(wù)器發(fā)出 現(xiàn)在看一下response的回復(fù)情況?
因?yàn)閞esponse本身很大,而客戶端的MSS只有1460 (上面看到的1506不是超過了MSS的意思,實(shí)際該數(shù)據(jù)包只有1424,加上48個(gè)字節(jié)的TCP包頭,20字節(jié)的ip包頭,14字節(jié)的以太網(wǎng)包頭一共是1506,正常tcp包頭為20字節(jié)因?yàn)檫@個(gè)tcp包被拆包了,所以包頭里多了28個(gè)字節(jié)的options)所以一個(gè)response被拆成了多個(gè)包。 通過報(bào)文不難看出這個(gè)response在網(wǎng)絡(luò)中傳輸大概花了1ms不到的時(shí)間(大概730微秒),因?yàn)榭吹绞沁^濾掉過端口(指定管道)的流量,實(shí)際上在這不到1ms的時(shí)間里另外的管道也是可能同時(shí)在接收數(shù)據(jù)的。 pipe之所以能比常規(guī)請(qǐng)求方式性能高出這么多,主要有以下幾點(diǎn) 1:管線式發(fā)送,每條request不要等response回復(fù)即可直接發(fā)送下一個(gè)(重點(diǎn)不在于使用的是同一條線路,而且不約等待回復(fù)) 2:多條請(qǐng)求打包發(fā)送,在網(wǎng)絡(luò)條件合適的情況下一個(gè)包可以包含多條request 3:只要服務(wù)器允許只需要?jiǎng)?chuàng)建極少tcp鏈接 (因?yàn)榉蔷钟蚓W(wǎng)的TCP線路一般都遵循慢啟動(dòng),網(wǎng)絡(luò)正常情況下需要一定時(shí)間后效率才能達(dá)到最高) 現(xiàn)在我們可以來說下pipe弊端 實(shí)際pipe早就被http1.1所支持,并且大部分nginx服務(wù)器也支持并開啟了這一功能。 相比普通的http keepalive傳輸 pipe http 解決了HOL blocking (Head-of-Line Blocking),而正是不再遵循一發(fā)一收的模式,使得應(yīng)用層不能直接將每個(gè)請(qǐng)求與回復(fù)一一對(duì)應(yīng)起來,對(duì)部分需要提交并區(qū)分返回結(jié)果的POST一類的請(qǐng)求,這種方式顯的不是很友好。 解決方法其實(shí)也很簡(jiǎn)單,在應(yīng)用服務(wù)上為request于response加上唯一標(biāo)簽即可以區(qū)分,或者直接使用HTTP2.0(https://tools.ietf.org/pdf/rfc7540.pdf)(這也是2.0的一個(gè)重要改進(jìn),http2.0也是通過類似的方式為其每個(gè)幀添加標(biāo)識(shí)當(dāng)前stream的id來實(shí)現(xiàn)區(qū)分的) 下面是pipe與常規(guī)http的簡(jiǎn)單對(duì)比| pipe 管線式HTTP | 普通HTTP 1.1 |
| 使用同一條tcp線路 | 使用不同鏈接(支持keepalive 可以保持鏈接) |
| 不用等待回復(fù)即可以直接發(fā)送下一個(gè)請(qǐng)求 | 同一個(gè)鏈接必須收到回復(fù)后才能發(fā)起下一個(gè)請(qǐng)求 |
| 一次/一包可以同時(shí)發(fā)送多個(gè)請(qǐng)求 | 一次只能發(fā)送一個(gè)請(qǐng)求 |
https://github.com/lulianqi/PipeHttp/?(工程地址) https://github.com/lulianqi/PipeHttp/tree/master/MyPipeHttpHelper?(類庫地址) https://github.com/lulianqi/PipeHttp/tree/master/PipeHttpRuner?(測(cè)試deom地址) 《新程序員》:云原生和全面數(shù)字化實(shí)踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀
總結(jié)
以上是生活随笔為你收集整理的超高性能管线式HTTP请求(实践·原理·实现)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Rainbond最佳实践:Tomcat配
- 下一篇: 地铁投资增值自我