在做系统优化的过程中,最常见的一种情况就是tcp并发连接数的调整。
首先了解一下Linux系统中tcp连接队列的情况,在内核2.2版本以后,listen队列分成两组SYN和ACCEPT,如下图
注意:两队列都是处于listen状态的队列
那么在实际情况下,我们如何判断这两个队列是排队情况。
在内核3.10版本之后,可以通过ss -tnlp的命令查询
当处于listen状态下,Recv-Q表示Accept Queue,Send-Q表示backlog参数。
那么backlog参数又是什么,我们参考Linux中的man手册关于listen函数listen(2) - Linux manual page
可以看到backlog就是限制最大的SYN和ACCEPT数量,所以,但发现排队的队列数量过大,就可能导致连接的并发数过低,当然前提的应用的性能足够高。
但有时候ss命令给出的数据并不完全准确,因为给出的统计是以秒为单位,出现队列满问题的窗口可能是非常短的,所以这时我们还得进行其他的信息辅助确认,通过以下命令可以查询出listen状态下被丢弃的包的数量
nstat -az TcpExtListenDrops
如果以上的输出不对增长,说明存在越来越多的listen的包被丢弃,也说明存在队列满的情况
这时候我们就可以提高内核参数进行优化
net.core.somaxconn
这时我们又提出一个问题,我们能不能知道是哪个应用出现了队列满的问题呢?
当然是可以的,我们参考文章
https://blog.cloudflare.com/syn-packet-handling-in-the-wild/
/*
* Prints details on specifically what connections
* suffered due to Accept Queue overflow. It can be greatly
* useful for identifying periodically hung applications that
* fails to accept() connections fast enough.
*
* Usage: stap acceptq.stp
*/
probe begin {
printf("time (us) \tacceptq\tqmax\tlocal addr\tremote_addr\n")
}
function skb_get_remote_v4addr:string(skb:long)
{
return format_ipaddr(__ip_skb_daddr(__get_skb_iphdr(skb)), 2 /* AF_INET */)
}
function skb_get_remote_v6addr:string(skb:long)
{
ipv6_hdr = &@cast(__get_skb_iphdr(skb), "ipv6hdr")
return format_ipaddr(&ipv6_hdr->daddr, 10 /* AF_INET6 */)
}
function skb_get_remote_port:long(skb:long)
{
return __tcp_skb_sport(__get_skb_tcphdr(skb))
}
probe kernel.function("tcp_v4_conn_request") {
if ($sk->sk_ack_backlog > $sk->sk_max_ack_backlog) {
printf("%d\t%d\t%d\t%s:%d\t%s:%d\n",
gettimeofday_us(),
$sk->sk_ack_backlog,
$sk->sk_max_ack_backlog,
inet_get_ip_source($sk),
inet_get_local_port($sk),
skb_get_remote_v4addr($skb),
skb_get_remote_port($skb));
}
}
probe kernel.function("tcp_v6_conn_request") {
if ($sk->sk_ack_backlog > $sk->sk_max_ack_backlog) {
printf("%d\t%d\t%d\t[%s]:%d\t[%s]:%d\n",
gettimeofday_us(),
$sk->sk_ack_backlog,
$sk->sk_max_ack_backlog,
inet_get_ip_source($sk),
inet_get_local_port($sk),
skb_get_remote_v6addr($skb),
skb_get_remote_port($skb));
}
}
将以上的内容存成脚本acceptq.stp,运行stap -v acceptq.stp,则可以输出端口对应的队列情况
参考链接
https://blog.cloudflare.com/syn-packet-handling-in-the-wild/