Listen backlog

#きっかけ
この記事を投稿しようと思ったきっかけですが、nginx+gnicorn+Djangoで開発をする機会がありまして、開発も終盤にさしかかりパフォーマンスチェックをしている際に問題が起きました。

abコマンドでベンチマークを取っていたんですが、各設定は

```bash
#nginx
worker_processes  2;
events {
    worker_connections  1024;
}

#gunicorn
bind = 'unix:/tmp/gunicorn.sock'
backlog = 2048
workers = 2
worker_class = 'sync'
```

abはこんな感じです

```bash
ab -n 5000 -c 200 http://domain.com/
```

これでベンチを取っていると、abでLengthエラー発生!!

原因を探ってみると、nginxのログに

```bash
connect() to unix:/tmp/gunicorn.sock failed (11: Resource temporarily unavailable)
```

問題のこれが発生。
connectエラーですね。
最初に疑ったのがファイルオープンの上限。

```bash
$ ulimit -a
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 7812
max locked memory       (kbytes, -l) 64
max memory size         (kbytes, -m) unlimited
open files                      (-n) 1024
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 10240
cpu time               (seconds, -t) unlimited
max user processes              (-u) 1024
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited
```

これの***open files***ですね。
ただ、よくよく考えると
そもそも***socket***をコールして正常にソケットディスクリプタができているなら、これはないなということ。
今考えると少し恥ずかしいです。

次にバックログ。

```bash
#man listen
LISTEN(2)                  Linux Programmer’s Manual                 LISTEN(2)

名前
       listen - ソケット(socket)上の接続を待つ

書式
       #include <sys/types.h>          /* 「注意」参照 */
       #include <sys/socket.h>

       int listen(int sockfd, int backlog);

説明
       listen() は sockfd が参照するソケットを接続待ちソケット (passive socket) として印を
       つける。接続待ちソケットとは、 accept(2) を使って到着した接続要求を受け付けるのに使
       用されるソケットである。

       sockfd  引き数は、 SOCK_STREAM 型か SOCK_SEQPACKET 型のソケットを参照するファイルデ
       ィスクリプタである。

       backlog 引き数は、 sockfd についての保留中の接続のキューの最大長を指定する。キュ ー
       が いっぱいの状態で接続要求が到着すると、クライアントは ECONNREFUSED というエラーを
       受け取る。下位層のプロトコルが再送信をサポートしていれば、要求は無視され、これ以 降
       の接続要求の再送信が成功するかもしれない。
```

manコマンドです。
これのバックログが少ないのかなと。
ただ、***gunicorn***のconfigで2048と設定しているのになぜ？という感じでした。
同じabコマンドで***gunicorn***を***localhost***で起動したベンチではこのエラーが発生しませんでした。

#原因
ここまで書くと気づいている方が多数だと思いますが、

```bash
$ sysctl net.core.somaxconn
net.core.somaxconn = 128
```

これです。
ここがデフォルトのままでした。
かなり勉強不足を露呈しているようで恥ずかしいですが、この値でbacklogが上書きされるみたいです。
ここを変更すればエラーが出ることなくベンチとれました。
この値の変更方法は、置いといて...

#とりあえずシステムコールの中身を見てみる
これを教訓にソケット周りのシステムコールを見ることに決めました。
ちなみにソースは、今更？***linux kernel 2.6***です。
ここからはソースをのせるだけになりますが、

実際にデフォルト値を設定しているのは、

```c
static __net_init int sysctl_core_net_init(struct net *net)
{
	struct ctl_table *tbl;

	net->core.sysctl_somaxconn = SOMAXCONN;

	tbl = netns_core_table;
	if (net != &init_net) {
		tbl = kmemdup(tbl, sizeof(netns_core_table), GFP_KERNEL);
		if (tbl == NULL)
			goto err_dup;

		tbl[0].data = &net->core.sysctl_somaxconn;
	}

	net->core.sysctl_hdr = register_net_sysctl_table(net,
			net_core_path, tbl);
	if (net->core.sysctl_hdr == NULL)
		goto err_reg;

	return 0;

err_reg:
	if (tbl != netns_core_table)
		kfree(tbl);
err_dup:
	return -ENOMEM;
}

#socket.h
/* Maximum queue length specifiable by listen.  */
#define SOMAXCONN	128
```

この辺ですかね。
思いっきし128って書いてますね。
そして肝心の上書き

```c
SYSCALL_DEFINE2(listen, int, fd, int, backlog)
{
	struct socket *sock;
	int err, fput_needed;
	int somaxconn;

	sock = sockfd_lookup_light(fd, &err, &fput_needed);
	if (sock) {
		somaxconn = sock_net(sock->sk)->core.sysctl_somaxconn;
		if ((unsigned)backlog > somaxconn)
			backlog = somaxconn;

		err = security_socket_listen(sock, backlog);
		if (!err)
			err = sock->ops->listen(sock, backlog);

		fput_light(sock->file, fput_needed);
	}
	return err;
}
```

```c
if ((unsigned)backlog > somaxconn)
    backlog = somaxconn;
```

ここで上書きされてますね。
色々勉強になりました。
もう少し掘り下げてみたいのですが、ここまでにしておきます。
ちなみに

```c
sock->ops->listen(sock, backlog);
```

ここで各AF_系の***listen***処理を行います。
例えば

```c
//AF_INET ipv4
int inet_listen(struct socket *sock, int backlog);
//AF_UNIX unix domain
static int unix_listen(struct socket *sock, int backlog);
```

こんな感じだと思います。
この辺もっと詳しく掘り下げてみたいです。

ざっと調べただけなので間違っている箇所もあると思います。
何か気づいたら指摘してください。