- はじめに
- 本題に入る前に
- Nagleアルゴリズムとは
- The small-packet problem の解決法としてのNagleアルゴリズム
- 遅延ACKの前に…
- 遅延ACK
- Nagleアルゴリズムと遅延ACK
- おわりに
はじめに
どうも、最近嫌な予感が段々と確信になっていくタイプのサイコホラー映画に弱いという事がわかったけんつです。
諸事情で、Linuxカーネルモジュールとして実装したEchoサーバのコードを見なおしていたらNagleアルゴリズムと遅延ACKというものに出会いました。
気になったので色々と調べているとその仕組みが面白いなと思ったのでまとめます。
Nagleアルゴリズムとは
ネーグルアルゴリズムと言うみたい。
RFC 896「IP/TCPインターネットワークにおける輻輳制御」*2で制定されており、その名の通り輻輳制御に関するアルゴリズムである。
そもそも、これが何故必要かということはある問題がキーになっているとRFCには記載されている。
The small-packet problem の解決法としてのNagleアルゴリズム
この問題を解決するためにNagleアルゴリズムは存在している。
Nagleアルゴリズムは以下の3つの条件を満たすまで、パケットをバッファにためて条件を満たした段階で送信することで効率的にデータを送信するアルゴリズムである。
つまり、「Nagleアルゴリズムでは最大セグメントサイズ以下の複数の送信メッセージを一つに束ね、まとめて送信する。特に、送信パケットで送信側が ACK を受け取っていないのがある場合、送信するに値するまで送信側はバッファリングを行い、そして、一度にまとめて送信する。」アルゴリズムとなる*3
- 未送信データが最大セグメントサイズ以上になる
- 過去の送信パケットで ACK が未受信の物がなくなる
- タイムアウトになる
このアルゴリズムで注意したい点は送信を遅延させる時間が200~500msであるという点(BDS系は最大200ms)。
これは次にまとめる遅延ACKとの相性が最高に悪く、オンラインゲームやその他のリアルタイム性を要求されるネットワークに於いて
そもそも、Nagleアルゴリズムがレイテンシを犠牲にしている分余計に遅延が発生してしまうという致命的な問題を発生させる。
遅延ACKの前に…
遅延ACKの前に前提としていくつかTCPについて知らなければならないことがあるのでそっちを先に紹介する。
ウィンドウ
TCPにはウィンドウという概念がある。
これは何かというと、TCPでは送信されてきたデータを一時的にバッファに溜め込んでアプリケーションはそこからデータを取り出し処理していく。
これをウィンドウといい、一度に溜め込める量をウィンドウサイズという。
そしてウィンドウサイズは、送信側が確認応答を待たずに一度に送信できる最大量でもある。
またこのウィンドウサイズはフロー制御によって変更される場合があり常に一定というわけではない。
スライディングウィンドウ
スライディングウィンドウについては以下のサイトがわかりやすくまとまっている。
beginners-network.com
このサイトでは、9000byteのデータを送信したい場合でウィンドウサイズが3000byteの場合を想定している。
まず9000 byteを 1セグメント 1000byte ずつ送信する場合3つのパケットを一度に送信できる。
受信側がACKを送信側に送った場合、3001byte~6000byte分のデータを送信する。
このように、相手側のウィンドウサイズに応じて送信するデータの範囲をずらしていくことをスライディングウィンドウという。
しかし、実際には1000byte送ってスライディングウィンドウすることも可能なのだがそれをしてしまうと1セグメントごとにACKを送る必要があるため
非常に効率が悪くなってしまうという問題が発生する。
これが次の遅延ACKに関連している。
遅延ACK
データを受信した側が即座にACKを返すと、フロー制御が働いて小さいウィンドウサイズが設定される場合がある。
送信側はそれに合わせてデータを送ってしまうと非常に効率が悪くなる。(これはSWS: Silliy WIndow Syndromeと呼ばれている)
そこで、このACKを意図的に送らせて、ネットワークを効率よく利用する方法が挙げられた。
それが遅延ACKとなる。
遅延させる場合は
- 2 * 最大セグメントサイズのデータを受信するまで確認応答しない。
- 上記以外の場合は最大200~500ms、ACKを遅延させる。
大体2セグメントごとにACKを送信するように遅延させる場合がおおい。(らしい)
Nagleアルゴリズムと遅延ACK
上記で紹介した、遅延ACKとNagleアルゴリズムが両方働くと遅延が余分に働く場合がある
特にサーバが遅延ACKのタイムアウトになるまでリクエストがストップしてしまうという場合。*4
これは以下のサイトにわかりやすい例が記載されている。
postd.cc
特にわかりやすい例を以下にあげる
アプリケーション: やあ、パケット1だよ。 HAProxy:<無反応で、2つ目のパケットを待っている> HAProxy:<そのうちACKするけど、まあいいか> アプリケーション:<無反応> アプリケーション:<ACKを待っているんだけど、ネットワークが混雑しているのかな> HAProxy:もう待ち疲れた。はい、ACKだよ。 アプリケーション:やった! じゃあ2つ目のパケットを送るよ。 HAProxy:よかった。これで終了だ。
送受信側が意図的に待機している時間があるが、そこで余分な200msが発生している。
これが、リアルタイム性を要求されている場合わりと致命的な遅延となってしまうのでNagleアルゴリズムを無効化するかTCP_CORKで遅延時間を制御する場合がある
おわりに
楽しかった。
*1:輻輳 | 用語集 | KDDI株式会社 http://www.kddi.com/yogo/%E9%80%9A%E4%BF%A1%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9/%E8%BC%BB%E8%BC%B3.html
*2:RFC 896 - Congestion Control in IP/TCP Internetworks https://tools.ietf.org/html/rfc896
*3:Nagleアルゴリズム - Wikipedia https://ja.wikipedia.org/wiki/Nagle%E3%82%A2%E3%83%AB%E3%82%B4%E3%83%AA%E3%82%BA%E3%83%A0
*4:TCPを(少しは)理解しておくべきその理由 | POSTD https://postd.cc/why-you-should-understand-a-little-about-tcp/