いっぱい接続したいの

とあるホストに、TCP接続を張っては切るという処理をぐるんぐるん繰り返すベンチマーク的なプログラムを書いて動かしました。

最初のうちは期待した通りの動作をしてるんですが、途中から対向のホストにTCP接続できなくなってエラー出まくり。

$ netstat -tna | grep TIME_WAIT | wc -l
28230

これが原因ぽい。

KERNEL_SOURCE/Documentation/networking/ip-sysctl.txt によれば、

ip_local_port_range - 2 INTEGERS
        Defines the local port range that is used by TCP and UDP to
        choose the local port. The first number is the first, the
        second the last local port number. Default value depends on
        amount of memory available on the system:
        > 128Mb 32768-61000
        < 128Mb 1024-4999 or even less.
        (snip)

ということで、計算してみると、

$ cat /proc/sys/net/ipv4/ip_local_port_range | awk '{print $2-$1}'
28232

さっき数えた TIME_WAIT の数とだいたい一致してる。

ここちょっとあやふやな記憶なんですが、コネクションテーブルの管理は、SIP:SPORT:DIP:DPORTをキーとしていたはず。で、今回のケースはSIP, DIP, DPORTが固定でSPORTだけが変化してって、28232個使い果たして一意性が保てなくなったんで、エラーになった、ってことだと思うす。実際、他のホストには接続張れましたし。

でで、
最初はTIME_WAITの滞留時間を短くすればいいのかなぁと思ったんすけど、TIME_WAITのコネクションを再利用するオプションがあったような記憶がうっすらあったので、さっきのip-sysctl.txtをgrepしたらお目当てのを発見。

tcp_tw_recycle - BOOLEAN
        Enable fast recycling TIME-WAIT sockets. Default value is 0.
        It should not be changed without advice/request of technical
        experts.

tcp_tw_reuse - BOOLEAN
        Allow to reuse TIME-WAIT sockets for new connections when it is
        safe from protocol viewpoint. Default value is 0.
        It should not be changed without advice/request of technical
        experts.

おお。この2つの値を変えて、件のベンチマーク的なプログラムを実行した結果は以下の通り。

recycle reuse netstat -tna | wc -l
0 0 →たくさん。繋げなくなりエラーになる。
0 1 →たくさん。エラーにはならない。
1 0 →最大で3000個ぐらいまで増えるけどすぐに減る。
1 1 →最大で3000個ぐらいまで増えるけどすぐに減る。

最初のエラーになるケースを除いては、秒あたりの接続数はどれも同じぐらい。

挙動だけ見るとrecycle,reuseが1,0の場合と1,1の場合のは同じぽいんですが、recycleだけを1にするべきなのか、両方1にするべきかはわからず…

↓の参考文書にも書いてますが、この設定はWebサーバなんかでも有効なんじゃないかと思います。特に、keepaliveをOffにしている場合とか。

以下、参考。

追記 2020-11-16