とある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は奥が深く、楽しい。勉強になった。