背景
我们线上有一个 dubbo 的服务,出现大量的 CLOSE_WAIT 状态的连接,这些 CLOSE_WAIT 的连接出现以后不会消失,这就有点意思了,于是做了一下分析记录如下。
首先从 TCP 的角度看一下 CLOSE_WAIT
CLOSE_WAIT
状态出现在被动关闭方,当收到对端 FIN 以后回复 ACK,但是自身没有发送 FIN 包之前。
所以这里的原因就很清楚了,出现永远存在的 CLOSE_WAIT
的连接是因为,收到了对端的 FIN 包,但是自己一直没有回复 FIN。通过抓包确实验证了这个的想法。
问题就落在了为什么没有回复 FIN,这是一个健康检查探测的请求,三次握手成功以后,探测服务会马上发送 FIN,理论上 dubbo 服务也会立刻回复 FIN,但是没有任何反应。
对于 dubbo 底层使用的 netty 来说,它就是一个普通的 tcp 服务端,无非就这几步:
- bind、listen
- 注册 accept 事件到 epoll
- epoll_wait 等待连接到来
- 连接到来时,调用 accept 接收连接
- 注册新连接的 EPOLLIN、EPOLLERR、EPOLLHUP 等事件到 epoll
- epoll_wait 等待事件发生
如果是没有发送 fin,有几个比较明显的可能原因。
- 第 2 步没有做,压根没有注册 accept 事件(可以排除,肯定有注册)
- 第 4 步没有做,连接到来时,netty 「忘了」调用 accept 把连接从内核的全连接队列里取走。这里的「忘」可能是因为逻辑 bug 或者 netty 忙于其他事情没有时间取走,这个待会验证
- 第 5 步没有做,取走了连接,三次握手