就是要你懂 TCP | 最經典的TCP性能問題

轉載:就是要你懂 TCP | 最經典的TCP性能問題

問題描述

某個PHP服務通過Nginx將后面的tair封裝了一下,讓其他應用可以通過http協議訪問Nginx來get、set 操作tair

上線后測試一切正常,每次操作幾毫秒,但是有一次有個應用的value是300K,這個時候set一次需要300毫秒以上。 在沒有任何并發壓力單線程單次操作也需要這么久,這個延遲是沒有道理和無法接受的。

問題的原因

是因為TCP協議為了做一些帶寬利用率、性能方面的優化,而做了一些特殊處理。比如Delay Ack和Nagle算法。

這個原因對大家理解TCP基本的概念后能在實戰中了解一些TCP其它方面的性能和影響。

什么是delay ack

由我前面的TCP介紹文章大家都知道,TCP是可靠傳輸,可靠的核心是收到包后回復一個ack來告訴對方收到了。

來看一個例子:

image.png

截圖中的Nignx(8085端口),收到了一個http request請求,然后立即回復了一個ack包給client,接著又回復了一個http response 給client。大家注意回復的ack包長度66,實際內容長度為0,ack信息放在TCP包頭里面,也就是這里發了一個66字節的空包給客戶端來告訴客戶端我收到你的請求了。

這里沒毛病,邏輯很對,符合TCP的核心可靠傳輸的意義。但是帶來的一個問題是:帶寬效率不高。那能不能優化呢?

這里的優化就是delay ack。

delay ack是指收到包后不立即ack,而是等一小會(比如40毫秒)看看,如果這40毫秒以內正好有一個包(比如上面的http response)發給client,那么我這個ack包就跟著發過去(順風車,http reponse包不需要增加任何大小),這樣節省了資源。 當然如果超過這個時間還沒有包發給client(比如nginx處理需要40毫秒以上),那么這個ack也要發給client了(即使為空,要不client以為丟包了,又要重發http request,劃不來)。

假如這個時候ack包還在等待延遲發送的時候,又收到了client的一個包,那么這個時候server有兩個ack包要回復,那么os會把這兩個ack包合起來立即回復一個ack包給client,告訴client前兩個包都收到了。

也就是delay ack開啟的情況下:ack包有順風車就搭;如果湊兩個ack包自己包個車也立即發車;再如果等了40毫秒以上也沒順風車,那么自己打個車也發車。

截圖中Nginx沒有開delay ack,所以你看紅框中的ack是完全可以跟著綠框(http response)一起發給client的,但是沒有,紅框的ack立即打車跑了

什么是Nagle算法

下面的偽代碼就是Nagle算法的基本邏輯,摘自wiki

if there is new data to send

if the window size >= MSS and available data is >= MSS

send complete MSS segment now

else

if there is unconfirmed data still in the pipe

enqueue data in the buffer until an acknowledge is received

else

send data immediately

end if

end if

end if

這段代碼的意思是如果要發送的數據大于 MSS的話,立即發送。

否則:

看看前面發出去的包是不是還有沒有ack的,如果有沒有ack的那么我這個小包不急著發送,等前面的ack回來再發送

我總結下Nagle算法邏輯就是:如果發送的包很小(不足MSS),又有包發給了對方對方還沒回復說收到了,那我也不急著發,等前面的包回復收到了再發。這樣可以優化帶寬利用率(早些年帶寬資源還是很寶貴的),Nagle算法也是用來優化改進tcp傳輸效率的。

如果client啟用Nagle,并且server端啟用了delay ack會有什么后果呢?

假如client要發送一個http請求給server,這個請求有1600個bytes,握手的MSS是1460,那么這1600個bytes就會分成2個TCP包,第一個包1460,剩下的140bytes放在第二個包。第一個包發出去后,server收到第一個包,因為delay ack所以沒有回復ack,同時因為server沒有收全這個HTTP請求,所以也沒法回復HTTP response(server等一個完整的HTTP請求,或者40毫秒的delay時間)。client這邊開啟了Nagle算法(默認開啟)第二個包比較小(140

這就是悲劇的核心原因。

再來看一個經典例子和數據分析

這個案例來自:http://www.stuartcheshire.org/papers/nagledelayedack/

案例核心奇怪的問題是,如果傳輸的數據是 99,900 bytes,速度5.2M/秒;

如果傳輸的數據是 100,000 bytes 速度2.7M/秒,多了10個bytes,不至于傳輸速度差這么多。

原因就是:

99,900 bytes = 68 full-sized 1448-byte packets, plus 1436 bytes extra

100,000 bytes = 69 full-sized 1448-byte packets, plus? 88 bytes extra

99,900 bytes:

68個整包會立即發送(都是整包,不受Nagle算法的影響),因為68是偶數,對方收到最后兩個包后立即回復ack(delay ack湊夠兩個也立即ack),那么剩下的1436也很快發出去(根據Nagle算法,沒有沒ack的包了,立即發)

100,000 bytes:

前面68個整包很快發出去也收到ack回復了,然后發了第69個整包,剩下88bytes(不夠一個整包)根據Nagle算法要等一等,server收到第69個ack后,因為delay ack不回復(手里只攢下一個沒有回復的包),所以client、server兩邊等在等,一直等到server的delay ack超時了。

挺奇怪和挺有意思吧,作者還給出了傳輸數據的圖表:

這是有問題的傳輸圖,明顯有個平臺層,這個平臺層就是兩邊在互相等,整個速度肯定就上不去。

如果傳輸的都是99,900,那么整個圖形就很平整:

回到前面的問題

服務寫好后,開始測試都沒有問題,rt很正常(一般測試的都是小對象),沒有觸發這個問題。后來碰到一個300K的rt就到幾百毫秒了,就是因為這個原因。

另外有些http post會故意把包頭和包內容分成兩個包,再加一個Expect參數之類的,更容易觸發這個問題。

這是修改后的C代碼

struct curl_slist *list = NULL;

//合并post包

list = curl_slist_append(list, "Expect:");

CURLcode code(CURLE_FAILED_INIT);

if (CURLE_OK == (code = curl_easy_setopt(curl, CURLOPT_URL, oss.str().c_str())) &&

CURLE_OK == (code = curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, timeout)) &&

CURLE_OK == (code = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &write_callback)) &&

CURLE_OK == (code = curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L)) &&

CURLE_OK == (code = curl_easy_setopt(curl, CURLOPT_POST, 1L)) &&

CURLE_OK == (code = curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, pooh.sizeleft)) &&

CURLE_OK == (code = curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback)) &&

CURLE_OK == (code = curl_easy_setopt(curl, CURLOPT_READDATA, &pooh)) &&

CURLE_OK == (code = curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L)) && //1000 ms curl bug

CURLE_OK == (code = curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list))

) {

//這里如果是小包就不開delay ack,實際不科學

if (request.size() < 1024) {

code = curl_easy_setopt(curl, CURLOPT_TCP_NODELAY, 1L);

} else {

code = curl_easy_setopt(curl, CURLOPT_TCP_NODELAY, 0L);

}

if(CURLE_OK == code) {

code = curl_easy_perform(curl);

}

上面中文注釋的部分是后來的改進,然后經過測試同一個300K的對象也能在幾毫米以內完成get、set了。

尤其是在Post請求將HTTP Header和Body內容分成兩個包后,容易出現這種延遲問題。

總結

這個問題確實經典,非常隱晦一般不容易碰到,碰到一次決不放過她。文中所有client、server的概念都是相對的,client也有delay ack的問題。 Nagle算法一般默認開啟的

參考文章:

https://access.redhat.com/solutions/407743

http://www.stuartcheshire.org/papers/nagledelayedack/

https://en.wikipedia.org/wiki/Nagle%27s_algorithm

https://en.wikipedia.org/wiki/TCP_delayed_acknowledgment

企業級互聯網架構Aliware,讓您的業務能力云化:https://www.aliyun.com/aliware

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,321評論 6 543
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,559評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,442評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,835評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,581評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,922評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,931評論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,096評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,639評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,374評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,591評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,104評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,789評論 3 349
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,196評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,524評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,322評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,554評論 2 379

推薦閱讀更多精彩內容

  • 就是要你懂 TCP-- 最經典的TCP性能問題 問題描述 某個PHP服務通過Nginx將后面的tair封裝了一下,...
    plantegg閱讀 888評論 0 0
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,826評論 18 139
  • 轉載:http://blog.csdn.net/sctq8888/article/details/7398967案...
    礪豪閱讀 3,730評論 1 5
  • 是的,北北第一次見到霞身體的時候,就突然意識到成長還包括身體猝不及防的成熟。只是,這樣的“猝不及防”帶來了不安,敏...
    圓小圈圈閱讀 334評論 0 0
  • 【禪語】 看蝴蝶美麗就可憐自己不是蝴蝶,見獅子威風就抱怨自己不是獅子,總道時運不濟,命運多舛。但,你是誰?你究竟...
    武漢如心閱讀 224評論 0 3