● TCPが遅延する話
TCPではいわゆるショートパット問題による輻輳を防ぐためのNagleアルゴリズム(RFC 896)がある。が、適当にTCP送受信してしまうとこれが遅延悪化を起こしてしまうことがある。例えば下記の例など。
俺の MQTTブローカー がこんなに遅いわけがない - Qiita
https://qiita.com/ryskiwt/items/551f5ad60d8c4cd14588
理由は、Nagleアルゴリズムとdelayed acknowledgmentとの相性が最悪だから。詳細はこのへんに詳しい。
Nagleアルゴリズムと遅延ACK - それが僕には楽しかったんです。
https://rabbitfoot141.hatenablog.com/entry/2018/11/18/003606
● Nagleアルゴリズムの無効化
setsockopt()でTCP_NODELAYを有効にするとNagleアルゴリズムを無効にできる。無効にするとショートパケット問題が生じるが、これはアプリケーションプロトコルレベルで対処したほうが手っ取り早い。具体的にはwrite()やwritev()システムコールで一度にたくさん送信するようにすればよい。
ググると「/proc/sys/net/ipv4/tcp_low_latency でグローバルに設定できる」と記したページがいくつか見つかるが、man 7 tcpにある通り、Linux-4.14からこのパラメータは何の意味も持たなくなっている(Linuxのソースコードも確認済み)
● delayed acknowledgmentの無効化
setsockopt()でTCP_QUICKACKを有効にするとdelayed acknowledgmentを無効にできる。ただし、無効にした場合、ACKと応答パケットを同時に送ったり、受信ウィンドウを効率よく使ったりすることができなくなる。結局これも、アプリケーションプロトコルレベルで応答を必要とするケースを減らしたり、スループットを求めるコネクションは別ソケットにする、などの対策が必要になる。
● 遅延量を調整できないの?
Nagleアルゴリズムはそもそも遅延する時間という概念を持っていない。
networking - How to set nagle timeout in linux kernel? - Server Fault
https://serverfault.com/questions/836675/how-to-set-nagle-timeout-in-linux-kernel
delayed acknowledgmentの遅延時間はLinx kernelが動的に決める。linux/include/net/tcp.hの「TCP_DELACK_MAX」「TCP_DELACK_MIN」がそれに当たる。40msecから200msecでスケールする(今どきのcpu archだと HZ はほぼ100以上のため)
#define TCP_DELACK_MAX ((unsigned)(HZ/5)) /* maximal time to delay before sending an ACK */コードの方を見た感じ、スケールのさせ方にもいろいろ手が入っているようで、おいそれと簡単に変更できる実装にはなっていない。非常によくわかっている場合を除いてパラメータを変更するのはおすすめできない。
#if HZ >= 100
#define TCP_DELACK_MIN ((unsigned)(HZ/25)) /* minimal time to delay before sending an ACK */
#define TCP_ATO_MIN ((unsigned)(HZ/25))
#else
#define TCP_DELACK_MIN 4U
#define TCP_ATO_MIN 4U
#endif
● 他のアプローチ
逆の、積極的に送信を遅延させるTCP_CORK(setsockopt()で設定)というオプションがある。TCP_NODELAYを有効にしたままTCP_CORKのON,OFFを切り替えることで、TCPを実際に送信するタイミングを調整することができる。もちろんMSSも意識しないといけない。ので、結局は、TCP_CORKなんて使わずに、ユーザランドでバッファリングしてwrite()システムコールで一度にたくさん書いたほうが手っ取り早いのかもしれない。
● TCPのタイムアウト
「SO_SNDTIMEO, SO_RCVTIMEOが使える」と書いたページも見つかるが、Linuxのコードを読む限り、これらはsocket全般に広く効くものの、ことTCPに関してはこれらは使われないように読めた。TCPは代わりにTCP_USER_TIMEOUTで設定するというように見える。でもごめんちょっと自信ない、誰か本当のとこ教えて。
● tcp keep alive
再送やらタイムアウトやらヘタに頑張ってほしくないと思いTCP_KEEPCNT, TCP_KEEPIDLE, TCP_KEEPINTVLを設定する例もたくさん見られる。が、やっぱりワナがあるようで、設定しといたほうがいいものの、過信はしないほうが良さげ。
TCPとタイムアウトと私 - Cybozu Inside Out | サイボウズエンジニアのブログ
https://blog.cybozu.io/entry/2015/11/26/170019
結局、アプリケーションプロトコルレベルでも検知して後始末する系をケアせざるを得ないようだ。
● ApacheのTCP_NODELAYの状況
Nagle algorythem / TCP_NODELAY をまとめる - Qiita
https://qiita.com/uturned0/items/3ab037d4d2d0500586f5
ではApacheがNagleアルゴリズムをどうしているのかわからないと記載されているが、コード見た感じ、問答無用でAPR_TCP_NODELAYを設定しているようですね。
● nginxのパフォーマンス
Apacheに比べて静的コンテツの配信に強いとされるnginx、その理由はApacheがpreforkなのに対してnginxはイベント駆動だ、とか書かれたページがいくつか見つかるものの、どうもそれだけじゃイマイチ納得できなかった。REUSEPORTとかが効いてんのかなと持って調べてみると、その程度の話ではなく、もはやcontext switchやCPU cache lineのレベルで性能差が出ているという話の模様。そのレベルまで性能引き出せているなら、確かに、無駄にプロセスやスレッドを増やしたりせず、CPUコア数に応じただけの数のworkerをぶん回らせた方が早そう。でもそれ、ネットワークやディスクがボトルネックじゃないという前提も必要よね。このへんに詳しかった。
linux - Is Nginx's approach to CPUs scalability (per process epoll event queue) optimal? - Stack Overflow
https://stackoverflow.com/questions/62234057/is-nginxs-approach-to-cpus-scalability-per-process-epoll-event-queue-optimal
Why does one NGINX worker take all the load?
https://blog.cloudflare.com/the-sad-state-of-linux-socket-balancing/
● その他(過去の記事)
Linux memo 2019/11/22
https://www.rarul.com/mt/log/2019/11/linux-memo-2019-8.html
TIME_WAIT関連のメモ
https://www.rarul.com/mt/log/2018/01/time-wait.html
● その他(ネットワークですらない雑記)
LD_BIND_NOWという環境変数が使える。man ld.so(8)とか、見たことないパラメータがいっぱいあるな...このうちどれが実用的なやつなのかも正直よくわからん。