2月末,微信群里突然收到监控告警,接口的无返回结果增多。nginx进行初步排查,发现请求内部服务A报了大量“连接失败”。我登录到服务器上看了下,服务A上的连接数超过了63000,应该是连接太多导致的请求失败,重启服务后请求正常了,监控恢复。一周之后同样的问题再次出现。。。不得不深入排查下了。

存在大量 ESTABLISHED连接 established状态的连接过多_服务端

为什么连接数一直在增长?为什么其他集群没有这个问题?为什么以前没有问题?是人性的泯灭还是。。。 一个个问题出现在我的头脑中

1. tcp连接的异常关闭导致服务端留下了大量ESTABLISHED状态的连接

对比了服务A上和nginx上的连接,服务A上的连接很多在nginx侧都不存在了。也就是说nginx侧关闭了连接,但是服务器A由于各种未知原因没能正常执行TCP的关闭,导致之前的连接以ESTABLISHED状态保留了下来。

2. 为什么其他集群没报这个问题呢?

理论上服务端没能正常关闭tcp连接也算正常,比如客户端直接奔溃或者直接断开网络,也不会发送tcp关闭的报文,服务端自然也觉察不到客户端的离开。这种情况下服务端会有tcp keepalive功能,该功能会探测一个建立好的连接是否正常,如果不正常就会删除该连接,释放资源。

从网上查到需要linux配置下面几个参数才行:

# sysctl -a | grep keepalive
net.ipv4.tcp_keepalive_intvl = 75
net.ipv4.tcp_keepalive_probes = 9
net.ipv4.tcp_keepalive_time = 900

出问题的机器和其他集群没出问题的机器的配置是一致的。两个集群用的代码也是一致的,为什么A服务有此问题呢 ?

一个命令看出了两个集群的服务上建立连接的差别:

//A服务上连接情况
#netstat -anop 
Proto Recv-Q Send-Q Local Address Foreign Address State   PID/Program name    Timer
tcp 0 10.114.253.221:3608 10.116.15.11:39628 ESTABLISHED 10063/bin/coopcxsch  off (0.00/0/0)

//其他集群连接情况
#netstat -anop 
Proto Recv-Q Send-Q Local Address Foreign Address State   PID/Program name    Timer
tcp 0 10.113.253.221:3608 11.116.15.11:39626 ESTABLISHED 10063/bin/coopcxsch  keepalive (9.32/0/0)

在Timer这一列,A服务上是off,其他集群是keepalive

这说明A服务上的连接是没开启keepalive功能的,无效的连接不会被清理,只会一直积累直到出问题。

可是代码都是同一份,怎么会一个开启了keepalive一个没开启呢 ?

最终发现是A服务编译用的Golang版本(1.10)跟其他集群用的Golang版本(1.16)不一致导致的,Golang从1.11开始默认开始了keepalive功能。升级了Golang版本后解决了问题

3. 为什么以前没有问题呢 ?

可能是nginx做过调整,调整后连接异常关闭的几率变大了,服务端积累的异常连接也越来越多。这里没有进一步定位了,只是猜测。

总结:

这次问题定位有两个收获,第一个是keepalive功能需要服务开启才生效,不能只看linux的配置;第二个是工具的使用很重要,刚开始查连接的时候我常用lsof -p 这个命令,这个命令可以方便地看到某个服务上连接有哪些,缺点是无法看到keepalive是否开启这种细节的东西,而netstat和ss命令可以看到keepalive的细节,补一个ss命令的回显信息:

#ss -aneo
tcp  ESTAB  0  0   10.13.217.181:3609 10.114.253.221:60142  timer:(keepalive,1min
2sec,0) ino:3635931755 sk:ffff882039fae800 <->