為什麼你得學些 TCP 的知識?

劉曉鵬發表於2015-12-17

這不是指要明白 TCP 的所有東西,也不是說要通讀 《TCP/IP 詳解》。不過懂一點 TCP 知識是很有必要的。理由如下:

當我還在 Recurse Center 的時候,我用 Python 寫過 TCP 協議棧(還寫過一篇文章:如果你用 Python 寫 TCP 協議棧會遇到什麼?)。這是一次有趣的學習經歷,但是也僅此而已。

一年以後,工作中有人在 Slack 上提到:“嘿,我在向 NSQ 釋出訊息時,每次要耗費 40 毫秒”。我已經斷斷續續思考了一個星期,但是沒有任何結果。

一點背景知識:NSQ 是一個訊息佇列,你通過本地的一個 HTTP 請求向其釋出訊息。傳送本地的一個 HTTP 請求確實不應該花費 40 毫秒,有時候會更差。NSQ 守護程式的負載不高,也沒有使用過多的記憶體,也看不到 GC 停頓。這究竟是為什麼呢?神吶,救救我吧!

突然我記起我一週以前看過的一篇叫做“效能研究(In search of performance)”的文章——我們如何為每個 POST 請求節省 200ms。在這篇文章中,他們說到為什麼每個 POST 請求會花費額外的 200 毫秒。就是這個原因。這是該文章中的關鍵段落:

延遲確認(ACK) 與 TCP_NODELAY

Ruby 的 Net::HTTP 會將 POST 請求切分為兩個 TCP 包,一個訊息頭,一個訊息體。相反,curl 會將這兩者合併為一個包。更糟糕的是,Net::HTTP 在開啟 TCP 套接字時不會設定 TCP_NODELAY,這將導致第二個包需要等到第一個包的接收確認通知之後才能傳送。這是 Nagle 演算法導致的。

轉換到連線的另一端,HAProxy 需要決定如何確認這兩個包。在 1.4.18 版本中(我們正在用的版本),它是通過 TCP 延遲確認通知來實現的。延遲確認對 Nagle 演算法有非常糟糕的影響,會導致請求暫停直到伺服器延遲確認超時。

現在我們解釋這個段落說的內容。

  • TCP 是一個通過資料包傳輸資料的演算法
  • 他們的 HTTP 庫將 POST 請求分割成兩個小的資料包傳送

接下來,TCP 採用類似如下的步驟進行互動:

application:Hi!這裡有一個資料包。
HAProxy:(沉默),等待第二個包傳送
HAProxy:對了,我需要返回一個確認,不過沒關係,等會吧
application: (沉默)
application:好吧,我正在等待確認,可能現在網路延遲比較大
HAProxy:好吧,太煩人了,這是一個確認。
application:好極了,這是第二個資料包!!!
HAProxy:親,我們已經搞定了。

這個過程是不是應用程式和 HAProxy 都在消極等待另一方傳送資訊?這就是那額外的 200ms。應用程式這麼做的是因為 Nagle 演算法,而 HAProxy 訊息等待的原因是延遲確認。

據我所知,延遲確認是所有 Linux 系統的預設行為。所以這不是一個偶然或者異常情況,如果傳送 TCP 資料包多一個 1 個,你就會遇到這種情況。

現在,我們成為專家了

讀過這篇文章之後我很快就忘了。不過當我被額外的 40 毫秒難住的時候,我又記起來了。

所以我認為——這不可能是我的問題,可能嗎?可能嗎??然後我發了一封郵件給我團隊說:“我想我快要瘋了,但是這可能是 TCP 的問題”。

所以我提交了一次修訂,將我的應該調整為 TCP_NODELAY,然後問題就“嘣”的一聲解決了。

40 毫秒的延遲立馬就消失了。所有的事情都解決了,我就是個天才。

我們是否應該完全停止使用延遲確認?

我剛好在 Hacker News 看到 John Nagle (Nagle 演算法的創始人)對 @alicemazzy 提到這個問題的評論。

本質問題是延遲確認。200 毫秒的“延遲確認”是一個非常不好的主意,1985 年中,在伯利克(Berkeley)研究 BSD 的人實際上沒有真正明白這個問題。延遲確認是應用層對 200 毫秒內是否響應的一場賭博,但是即便每次它都賭輸了,TCP 仍在使用延遲確認。

他繼續說到,確認本身是很小並且消耗很低的,延遲確認引起的問題可能比它解決的問題還要多。

不懂得 TCP 你就無法解決 TCP 問題

我曾經也認為,TCP 是一個相當底層的問題,我不需要明白。大多數時候你的確不需要明白。但是有的時候,當你在實踐中遇到由於 TCP 演算法引起的 bug 時,懂點 TCP 知識就變得非常重要了。(正如我們經常在部落格中討論的,許多事情都是這樣,比如系統呼叫和作業系統:) )

延遲確認及 TCP_NODELAY 的互動非常不好——這對任何語言實現的 HTTP 請求都有影響。你不需要很深入的去了解,成為系統程式專家。但是瞭解一點 TCP 是如何運作的,對我的工作的確大有裨益。通過對 TCP 的學習,我才意識到這篇部落格所描述的問題也許正好是我所熟悉的領域。我也一直在使用 strace,並且會一直使用下去。

相關文章