本文来自 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 可以监控系统全局的 ListenDropsListenOverflow。 通过调整实现了用少量服务器提供大流量的目标。

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 Listen backlog graph

减小 Apache 的可用 worker 数,动态降低 net.core.somaxconn 的值,然后用 siege 压测,得到了上图的曲线。 可以看到一个个连接在等待从队列中取出,交由 Apache 处理,很有趣。

** 一般来说,这个应该保持为零,或者非常非常低的值。 如果升高并且保持不下,则说明了 Apache 不足以处理接收到的请求量级。 **

References: