本文来自 Ryan Frantz 的一篇博文。 文章介绍了针对 Apache 的 TCP Backlog 做调优的过程中遇到的问题,和最终的惊喜发现。
Apache TCP Backlog
TCP Listen Backlog
创建 TCP socket 时可以定义接收 backlog 的值。
用于确定等待被进程 accept()
的 fully acknowledged(SYNC->SYNC/ACK->ACK) 最大连接数。
请求处理足够快时,backlog 值应该为0,或者很低。
Apache ListenBacklog
Apache 中默认值为 511,使用 ListenBacklog
语法调整。
Linux kernel 则有另外的处理:
如果 ListenBacklog
值超过 sysctl 设置的 net.core.somaxconn
(默认128),内核自动将它降到 sysctl 的值。
Tuning Apache and ss
作者在项目中维护仅有的几台 Apache 服务(~3000请求每秒,2或4台服务器)。 服务器比较少,需要优化 Apache 和内核。
开始调整 Apache 配置,使用 siege 压测性能。
基于 TCP listen backlog 知识,调整了 net.core.somaxconn
,以及 Apache 服务器的 Workers 参数。
期望的是 ss
能显示测试中的 TCP listen backlog,当时没达到效果。
之后的工作主要是为了显示单个 socket 的 listen backlog。
此外,通过测试找到 net.core.somaxconn
和 Apache 配置的平衡点,请求全被处理,而且连接没有被丢弃。
至少,使用 netstat
可以监控系统全局的 ListenDrops
和 ListenOverflow
。
通过调整实现了用少量服务器提供大流量的目标。
Trawling the Kernel and ss Source
作者开始分析内核源码,从中发现了蛛丝马迹。
首先在 include/net/sock.h
中直接发现一行说明:
* @sk_ack_backlog: current listen backlog
net/ipv4/tcp.c
里有个方法 tcp_get_info()
,将 TCP 状态信息展示到名为 tcp_info
的结构体。
如果 socket 处于 LISTEN
状态,它会将 listen backlog 的值映射为叫做 tcpi_unacked
的属性:
if (sk->sk_state == TCP_LISTEN) {
info->tcpi_unacked = sk->sk_ack_backlog;
抱着 ss
能派上用场的希望,作者查看了它的源码(来自 iproute2-3.10),并发现确实能行。
if (info->tcpi_unacked)
printf(" unacked:%u", info->tcpi_unacked);
原来是 backlog 在这里使用了另外的名字(unacked)。
Displaying Backlogged Connections with ss
既已知 backlog 里的连接被定义为 unacked,则可以使用 ss
查看 HTTP socket 是否在 backlog:
[root@apache01:~] $ ss -lti '( sport = :http )'
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 223 511 *:http *:*
rto:1000 mss:536 cwnd:10 unacked:223
可看出有 223 个 socket 处于 unacked(backing up)。
获得以上输出时正在用 siege
压测 Apache。
Finally, Mutha$%^&@n’ Graphs!
减小 Apache 的可用 worker 数,动态降低 net.core.somaxconn
的值,然后用 siege
压测,得到了上图的曲线。
可以看到一个个连接在等待从队列中取出,交由 Apache 处理,很有趣。
** 一般来说,这个应该保持为零,或者非常非常低的值。 如果升高并且保持不下,则说明了 Apache 不足以处理接收到的请求量级。 **