Tomcat 调优之从 Linux 内核源码层面看 Tcp backlog( 二 )


建连主要流程如下:

  1. 客户端向服务端发送 SYN 包请求建立连接 , 发送后客户端进入 SYN_SENT 状态
  2. 服务端收到客户端的 SYN 请求 , 将该连接存放到半连接队列(Syn queue)中 , 并向客户端回复 SYN + ACK , 随后服务端进入 SYN_RECV 状态
  3. 客户端收到服务端的 SYN + ACK 后 , 回复服务端 ACK 并进入 ESTABLISHED 状态
  4. 服务端收到客户端的 ACK 后 , 从半连接队列中取出连接放到全连接队列(Accept queue)中 , 服务端进入 ESTABLISHED 状态
  5. 服务端程序调用 accept() 方法 , 从全连接队列中取出连接进行处理请求
连接队列大小上述提到了半连接队列、全连接队列 , 这两队列都有大小限制的 , 超过的连接会被丢掉或者返回 RST 包 。
半连接队列大小主要受:listen backlog、somaxconn、tcp_max_syn_backlog 这三参数影响
全连接队列大小主要受:listen backlog 和 somaxconn 这两参数影响
tcp_max_syn_backlog 和 somaxconn 都是 linux 内核参数 , 在 /proc/sys/net/ipv4/ 和 /proc/sys/net/core/ 下 , 可以通过 /etc/sysctl.conf 文件来修改 , 默认值为 128 。
listen backlog 参数其实就是我们调用 listen 函数时传入的第二个参数 。回到主题 , Tomcat 的 accept-count 其实最后就会传给 listen 函数做 backlog 用 。
int listen(int sockfd, int backlog);可以在配置文件中配置 tomcat accept-count 大小 , 默认为 100
Tomcat 调优之从 Linux 内核源码层面看 Tcp backlog

文章插图
以下代码注释中也注明了 acceptCount 就是 backlog
Tomcat 调优之从 Linux 内核源码层面看 Tcp backlog

文章插图
以 Nio2Endpoint 为例看下代码 , bind 方法首先会根据配置的核心线程数、最大线程数创建 worker 线程池 。然后调用 jdk nio2 中的 AsynchronousServerSocketChannelImpl 的 bind 方法 , 该方法内会调用 Net.listen() 进行 socket 监听 。通过这几段代码 , 我们可以清晰的看到 Tomcat accept-count = Tcp backlog , 默认值为 100 。
Tomcat 调优之从 Linux 内核源码层面看 Tcp backlog

文章插图

Tomcat 调优之从 Linux 内核源码层面看 Tcp backlog

文章插图

Tomcat 调优之从 Linux 内核源码层面看 Tcp backlog

文章插图
上面说到了半全两个连接队列 , 至于这两个连接队列大小怎么确定 , 其实不同 linux 内核版本算法也都不太一样 , 我们就以 v3.10 来看 。
以下是 linux 内核 socket.c 中的源码 , 也就是我们调用 listen() 函数会执行的代码
/* * Perform a listen. Basically, we allow the protocol to do anything * necessary for a listen, and if that works, we mark the socket as * ready for listening. */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 int)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;}可以看到 , 此处会拿内核参数 somaxconn 和 传入的 backlog 做比较 , 取二者中的较小者作为全连接队列大小 。
全连接队列大小 = min(backlog, somaxconn) 。
接下来 backlog 会依次传递给如下函数 , 格式约定(源代码文件名#函数名)

经验总结扩展阅读