TIME_WAIT状態によるポート枯渇対策

結局Bind例外を発生させるのは負荷が高くなることにより消費されるTCPソケットのポートの枯渇が原因であった。
先日のエントリではシステムのTCP/IPパラメタを設定することで回避できると書いたが、冷静になって考えてみると根本にあるのTCP接続/切断を繰り返しているその作りの悪さだということに気が付く。
そもそもその問題を引き起こしたアプリケーションはサーバとの接続を決まった範囲のポートでしか行わない訳で、ならば接続/切断を繰り返す必要は全く無い。ということでアプリケーションを修正し、使用するポートの範囲が決まったならば接続したが最後システムダウンまでは切断しないで使いまわすようにした。これでTIME_WAIT状態のソケットは激減した。

しかし、TIME_WAIT状態のソケットはまだ大量に発生し続けている。何故か? TCPソケットは別にそのアプリケーションだけで使っている訳ではなく、JavaベースのWebアプリケーションサーバであれば少なくとも

  • 他のWebサーバへの接続など
  • JDBCドライバによるデータベースとの接続 (Oracleが相手であればポート1521等)

これらも全く同様に接続を消費している訳で、同様にTIME_WAIT状態のソケットを作り出していたのだ。そう考えるとやはりWindowsプラットホームのTCPパラメタのうち、1.使用できるローカルポート番号の上限、2.TIME_WAIT状態から接続を破棄するまでの時間はそれぞれ

1. HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\MaxUserPort = 5000
2. HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\TcpTimedWaitDelay = 240(秒)

だが、これら値の組合わせはクライアントO/Sとしては適切かもしれないが、サーバとしては厳しい値だと思われる。例えば一般的なLinuxの既定値は

1. /proc/sys/net/ipv4/ip_local_port_range = 1024〜4999 4000(個)
2. /proc/sys/net/ipv4/tcp_fin_timeout =  60(秒)

であり、1.はWindowsと同様だが2.が60秒と4分の1でしか無い。Windowsもサーバとして使うにはTcpTimedWaitDelayを60秒程度にするのが良いだろう。