一个因为网卡 txqueuelen 参数值太小导致丢包的 case

前段时间线上的一个服务出现 tcp 重传特别严重,同时客户端与服务端通信的过程中经常出现 io timeout 错误。最后发现是因为网卡 txqueuelen 参数的值设置的太小导致的。

简单记录一下当时的现象,万一将来遇到类似的现象的时候有个参考,整体不是很完整只给自己备忘一下,有空的时候再完善。

客户端

客户端程序使用 go 写的,与服务端端之间以 http 长连接的方式进行通信,直观的现象是日志中经常出现 read tcp <ip>:<port> -> <ip>:<port>: i/o timeout 错误。

由于没有客户端服务器的权限所以没有在客户端抓包。

服务端

服务端最先观察到的现象是客户端在频繁的重连(这里只的是频繁的发送 http 请求,前面讲到了客户端与服务端是以 http 长连接的方式通信的,正常情况是连上之后只发一个 http 请求,要等很久才会发其他的 http 请求,也就是不会出现频繁的发送 http 请求的情况)。

然后查看服务端主机的系统监控,发现 tcp 重传的指标特别高。

服务端抓包发现的异常情况是: 服务端丢包重传一段时间后,客户端发起了关闭连接的操作,然后又没有完成最后 ACK 而是直接 RST。

例子:

server -> client    [PSH, ACK] Seq=6195 Ack=1 Win=59 Len=38
server -> client    [TCP Retransmission] [PSH, ACK] Seq=6195 Ack=1 Win=59 Len=38
server -> client    [TCP Retransmission] [PSH, ACK] Seq=6195 Ack=1 Win=59 Len=38
server -> client    [TCP Retransmission] [PSH, ACK] Seq=6195 Ack=1 Win=59 Len=38
server -> client    [TCP Retransmission] [PSH, ACK] Seq=6195 Ack=1 Win=59 Len=38
server -> client    [TCP Retransmission] [PSH, ACK] Seq=6195 Ack=1 Win=59 Len=38
client -> server    [FIN, ACK] Seq=1 Ack=6233 Win=60 Len=0
server -> client    [PSH, ACK] Seq=6233 Ack=2 Win=59 Len=190
server -> client    [FIN, ACK] Seq=6423 Ack=2 Win=59 Len=0
client -> server    [RST] Seq=2 Win=0 Len=0
client -> server    [RST] Seq=2 Win=0 Len=0

通过 ifconfig 查看网卡状态,可以发现 RX dropped 的值特别高:

$ ifconfig

eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 10.0.x.x  netmask 255.255.255.0  broadcast 10.0.x.255
        inet6 xxxx  prefixlen 64  scopeid 0x20<link>
        ether xxx  txqueuelen 1000  (Ethernet)
        RX packets xxx  bytes xxx (68.2 TiB)
        RX errors 0  dropped 6485541  overruns 0  frame 0
        TX packets xxx  bytes xxx (68.5 TiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

最后公司网络&虚拟化团队说是因为虚拟化时网卡的 txqueuelen 参数设置太小导致的丢包,加大了 txqueuelen 的值后丢包问题就大大缓解了(客户端没有再重连了)。

这篇文章写的不是很详细,有很多证据没法拿出来或者说是缺失了,期望将来能找个时间以及找到一个可以简单在本地重现的方法,那个时候再完善这篇文章,暂时只能算是简单的 case 备忘。

BTW, 还碰到一个丢包的 case 是:最终通过开启网卡多队列以及中断绑定来解决大流量下的网络丢包问题。


Comments