Nagle算法node
Nagle算法是爲了提升帶寬利用率而設計的算法,該算法主要用於避免過多小分節報文在網絡中傳輸。好比一個20字節的TCP首部+20字節的IP首部+1個字節的數據組成的TCP數據報,有效傳輸通道利用率只有將近1/40。若是網絡充斥着這樣的小分組數據,則網絡資源的利用率是至關低下的。算法
若是開啓了這個算法 (TCP默認開啓),則協議棧會累積數據直到如下兩個條件之一知足的時候才真正發送出去:(1)積累的數據量達到MSS (2)有未確認的包存在時收到了一個 Ack包緩存
Nagle算法在維基百科上用僞代碼描述以下:服務器
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
反作用網絡
在介紹Nagle算法的反作用前要引入TCP的另一個機制TCP Delayed Acknoledgement,這個機制也是以減小網絡中小包爲目標被設計出來的。它的做用就是延遲 Ack 包的發送,使得協議棧能夠把Ack包和其它Ack包或數據包合併發送,提升網絡性能。固然Delayed Ack是有個超時機制的,超時時間是40ms~200ms。併發
然而Nagle算法和TCP Delayed Ack算法的組合有可能下降網絡性能:tcp
若是一個TCP鏈接的A端和B端啓用了 Nagle‘s Algorithm,而交互的數據包又小於MSS,則可能會出現這樣的狀況:A端在發送新數據前發現還有數據的ACK包沒有收到,那麼A端打算等到收到ACK包後再發送新數據,同時B端卻由於ACK包太少延遲了這些ACK包的發送,這樣有些包的耗時頗有可能大於40ms。性能
那麼若是Nagle算法和TCP Delayed Ack算法有如此大的反作用,TCP爲什麼會默認啓動它呢。由於最多見的write-read-write-read模式不會觸發這個反作用,只有write-write-read模式會受影響。大數據
write-read-write-read模式:A端第1次write,因爲沒有未確認的數據,數據被馬上發送出去。B端的ACK包雖然不會馬上返回,是跟隨處理完的響應數據一塊兒發回A端的,可是並不影響整個流程,由於A端反正要等待響應數據纔會繼續下去,以後的每次write-read交互過程都是如此。spa
write-write-read模式:這種場景業務邏輯層進行了分包,也就是說B端要等到A端的2個write包到達纔會響應,例如爬蟲把HTTP Header和Body分開發送。下面來模擬這一模式,A端第1次write,數據被馬上發送出去,第2次write由於第1次的包的Ack還沒回來,被緩存了下來等待Ack,這一等極可能就是40ms甚至200ms。那B端爲何遲遲不回Ack呢?由於Ack包也被緩存下來了,準備等A端的2次write到齊了連同響應數據一塊兒發回A端。這種狀況很像操做系統死鎖的循環等待條件,A端在等待B端的Ack才發第2次write,B端在等待A端的第2次write才發Ack(連同兩個write的響應)。
解決辦法
TCP_NODELAY選項
顧名思義,TCP_NODELAY套接字選項能夠關閉Nagle算法,若是咱們在程序的邏輯層能夠避免過小包的存在,徹底能夠關閉Nagle算法,避免不可預期的網絡延遲問題,尤爲是對響應的實時性比較高的系統。
int enable = 1; setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (void*)&enable, sizeof(enable));
Nginx也有tcp_nodelay配置來關閉Nagle算法。
題外話
Nagle算法是爲了不過小的包占用網絡帶寬,而有一個選項TCP_CORK能夠看作Nagle的極端版本。TCP_CORK讓TCP儘量的進行數據的合併組包,以最大MTU傳輸,若是發送的數據包大小太小則若是在0.6~0.8S範圍內都沒能組裝成一個MTU時,直接發送。這個策略通常用在文件服務器等只傳輸大數據包的場景中。