最近在分析系统的时候,发现在压测的时候服务端出现了比较多的CLOSE_WAIT状态的tcp连接,
需要等一会才能消除掉。。。。
恩,CLOSE_WAIT状态的tcp状态是怎么出现的呢。。:
首先客户端首先调用的close方法,那么将会发送fin数据包给服务端。。。
服务端收到fin数据包之后,将会返回ack确认,然后服务器的tcp连接就进入了CLOSE_WAIT状态了,这个时候服务端的socket再调用一次close,那么服务器将会给客户端发送fin数据包,这个时候服务器进入LAST_ACK状态,等待客户端返回ack数据包。。。
当服务端收到了客户端的ack数据包之后,那么对于服务端来说就相当于连接关闭了,可以释放文件描述符资源啥的了。。但是客户端在发送了最后一个ack之后需要进入TIME_WAIT状态,因为有可能服务端没有收到ack数据包,将会超时重传fin数据包。。。。
那么在压测时候,因为是客户端首先调用close方法,但是服务端却又很多CLOSE_WAIT状态的tcp连接,那么说明服务端并没有调用close方法,
但是自己代码里面又确实调用了gevent的socket的close方法,那么出现这种情况的原因是什么呢。。?
先来看一下gevnet的socket的close方法的实现:
# 注意,这里其实并没有调用socket的close def close(self, _closedsocket=_closedsocket, cancel_wait_ex=cancel_wait_ex): # This function should not reference any globals. See Python issue #808164. self.hub.cancel_wait(self._read_event, cancel_wait_ex) self.hub.cancel_wait(self._write_event, cancel_wait_ex) self._sock = _closedsocket()
那么,引用计数变成0,就将会被python收了。。。。
那么看看Python的socket模块的析构是怎么写的吧:
static void sock_dealloc(PySocketSockObject *s) { if (s->sock_fd != -1) (void) SOCKETCLOSE(s->sock_fd); if (s->weakreflist != NULL) PyObject_ClearWeakRefs((PyObject *)s); Py_TYPE(s)->tp_free((PyObject *)s); }
因此可以知道,如果sock正常被回收的话,肯定是会调用close的,那么这里并没有调用。。只能说明还有对底层python的socket对象的引用,恩。。确实是这样子的。。。因为代码循环引用,而且直接引用了底层socket,所以导致底层sock并没有及时释放,从而导致服务端有很多CLOSE_WAIT状态的连接。。。
我擦。。还好这其实对于服务端来说没问题并不大。。。不会引起什么大问题。。。改了一下就好了。。。。