Nikushi's blog Our Time is Limited.

Listen Backlog Queue Tuning

とあるdaemonが動作するサーバでたまにトラフィックが落ち込む事象があり、調べてみるとSYNセグメントを取りこぼす事象が起きていた。さらに調べてみるとどうもlisten backlogの値が小さいことが分かったのでメモ。 サーバがSYNパケットを受け取るとTCPのlisten backlog queue に格納しaccept()されるまでキューイングする。ソケットはLISTENからSYN_RCVDに遷移しますがbacklog queue以上をためることはできないので、SYNパケットが瞬間的にバーストしたりするとSYNパケットを取りこぼしちゃう。 listen backlog queue sizeはlisten()システムコールを実行するときに指定できるみたい。
$ man 2 listen
SYNOPSIS
  #include 

  int listen(int sockfd, int backlog);
さてそこでdaemonのソースコードを確認してみた。しかし、listenの第2引数には10000以上の大きな値が設定されているようで、この数を越えるリクエストがあるとは到底思えない。 もう一度manを見なおした。
$ man 2 listen
BUGS
  If the socket is of type AF_INET, and the backlog argument is greater than the constant SOMAXCONN (128 in Linux 2.0 & 2.2), it is silently truncated to SOMAXCONN.
listen()で指定したback log queue sizeがsomaxconnより大きい場合、somaxconnの値に切りつめられる、ということ。これはBUGなんだろうか。 確認してみると確かに小さい。
$ cat /proc/sys/net/core/somaxconn
 128
値を増やして様子を見ることにした。 ネット上の情報を探してみると今回のdaemonに限らずapacheはmemcachedなどlisten()するのは基本的にこれにはまる可能性があるみたい。
 # sysctl -w net.core.somaxconn=1024
 net.core.somaxconn = 1024
 $ /sbin/sysctl -n net.core.somaxconn
 1024
他に確認するところとしては、somaxconnとは少し違うが、net.ipv4.tcp_max_syn_backlog の値とかも確認するとよさそう。 サーバのbacklog queueにどのくらい溜まっているかは SYN_RCVDを数えればいい(間違ってたら指摘ください)
$ netstat -an |grep SYN_RCVD | wc -l
15
TCPは奥が深く、楽しい。勉強になった。