在TCP/IP協議中,無論發送多少數據,總是要在數據前面加上協議頭,同時,對方接收到數據,也需要發送ACK表示確認。為了盡可能的利用網絡帶寬,TCP總是希望盡可能的發送足夠大的數據。這里就涉及到一個名為Nagle的算法,該算法的目的就是為了盡可能發送大塊數據,避免網絡中充斥着許多小數據塊。
TCP_NODELAY就是用於啟用或關於Nagle算法。如果要求高實時性,有數據發送時就馬上發送,就將該選項設置為true關閉Nagle算法;如果要減少發送次數減少網絡交互,就設置為false等累積一定大小后再發送。默認為false。
淺談tcp_nodelay的作用
今天在用nginx作web緩存的時候,發現在http里加入這樣個參數,能有效的提高數據的實時響應性,那就是tcp_nodelay.下面我們來說說tcp_nodelay的原理:
TCP_NODELAY和TCP_CORK基本上控制了包的“Nagle化”,這里我們主要講TCP_NODELAY.Nagle化在這里的含義是采用Nagle算法把較小的包組裝為更大的幀。JohnNagle是Nagle算法的發明人,后者就是用他的名字來命名的,他在1984年首次用這種方法來嘗試解決福特汽車公司的網絡擁塞問題(欲了解詳情請參看IETF RFC 896)。他解決的問題就是所謂的silly window syndrome,中文稱“愚蠢窗口症候群”,具體含義是,因為普遍終端應用程序每產生一次擊鍵操作就會發送一個包,而典型情況下一個包會擁有一個字節的數據載荷以及40個字節長的包頭,於是產生4000%的過載,很輕易地就能令網絡發生擁塞,。Nagle化后來成了一種標准並且立即在因特網上得以實現。它現在已經成為缺省配置了,但在我們看來,有些場合下把這一選項關掉也是合乎需要的。
現在讓我們假設某個應用程序發出了一個請求,希望發送小塊數據,比如sns游戲中的點擊確定按鈕。我們可以選擇立即發送數據或者等待產生更多的數據然后再一次發送兩種策略。如果我們馬上發送數據,那么交互性的以及客戶/服務器型的應用程序將極大地受益。例如,當我們正在發送一個較短的請求並且等候較大的響應時,相關過載與傳輸的數據總量相比就會比較低,而且,如果請求立即發出那么響應時間也會快一些。以上操作可以通過設置套接字的TCP_NODELAY選項來完成,這樣就禁用了Nagle算法,在nginx中設置tcp_nodelay on,注意放在http標簽里。
另外一種情況則需要我們等到數據量達到最大時才通過網絡一次發送全部數據,這種數據傳輸方式有益於大量數據的通信性能,典型的應用就是文件服務器。應用Nagle算法在這種情況下就會產生問題。但是,如果你正在發送大量數據,你可以設置TCP_CORK選項禁用Nagle化,其方式正好同TCP_NODELAY相反(TCP_CORK 和 TCP_NODELAY 是互相排斥的)。下面就讓我們仔細分析下其工作原理。
假設應用程序使用sendfile()函數來轉移大量數據(nginx里可以設置sendfile on)。應用協議通常要求發送某些信息來預先解釋數據,這些信息其實就是報頭內容。典型情況下報頭很小,而且套接字上設置了TCP_NODELAY。有報頭的包將被立即傳輸,在某些情況下(取決於內部的包計數器),因為這個包成功地被對方收到后需要請求對方確認。這樣,大量數據的傳輸就會被推遲而且產生了不必要的網絡流量交換。
但是,如果我們在套接字上設置了TCP_CORK(可以比喻為在管道上插入“塞子”)選項,具有報頭的包就會填補大量的數據,所有的數據都根據大小自動地通過包傳輸出去。當數據傳輸完成時,最好取消TCP_CORK選項設置給連接“拔去塞子”以便任一部分的幀都能發送出去。這同“塞住”網絡連接同等重要。
總而言之,如果你肯定能一起發送多個數據集合(例如HTTP響應的頭和正文),那么我們建議你設置TCP_CORK選項,這樣在這些數據之間不存在延遲。能極大地有益於WWW、FTP以及文件服務器的性能,同時也簡化了你的工作.
最近用了用libsoup庫,用它來寫個HttpServ玩。結果發現性能不行,竟然比nginx/apache慢了20倍。拿wireshark一看,發現每個TCP回復都要耗時幾百毫秒。這就一下子讓我想到了Nagle算法(一般的Nagle算法都是延時200ms)。於是下載libsoup的庫,看代碼,果然沒有setsockopt。於是設置了TCP_NODELAY,結果速度一下子就上去了,與nginx相差無幾(WEB主要是耗IO,在高並發的時候才能顯示出代碼優化的好處)。
而這個Nagle算法,在Windows上的效果尤其明顯。以前使用GIO中的GSocket,也是如此。
對於初學socket編程的人來說,可能會忘記這里還有個“陷阱”。Nagle算法適用於小包、高延遲的場合,而對於要求交互速度的b/s或c/s就不合適了。socket在創建的時候,默認都是使用Nagle算法的,這會導致交互速度嚴重下降,所以需要setsockopt函數來設置TCP_NODELAY為1.
不過取消了Nagle算法,就會導致TCP碎片增多,效率可能會降低。所以,這也是要有所取舍的。