2月末,微信群里突然收到监控告警,接口的无返回结果增多。nginx进行初步排查,发现请求内部服务A报了大量“连接失败”。我登录到服务器上看了下,服务A上的连接数超过了63000,应该是连接太多导致的请求失败,重启服务后请求正常了,监控恢复。一周之后同样的问题再次出现。。。不得不深入排查下了。
为什么连接数一直在增长?为什么其他集群没有这个问题?为什么以前没有问题?是人性的泯灭还是。。。 一个个问题出现在我的头脑中
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 <->