微软 mysql java wordpress Ubuntu centos shell HTML5 程序员 开源 Firefox Python php Android Windows linux命令 apache nginx google linux

Web 服务器 TCP TIME_WAIT连接

在一台负载不低的web服务器,通过netstat –an查看到不少的TIME_WAITM网络连接。TIME_WAIT状态,查看下图。

关于TIME_WAIT,我摘了“tcp/ip详解“第18章”tcp连接的建立与终止“里面一段话来解释TIME_WAIT连接。
“TIME_WAIT状态也称为2MSL等待状态。当TCP执行一个主动关闭,并发回最后一个ACK,此连接必须在TIME_WAIT状态停留2倍的MSL(参见下文)。这样可以让TCP再次发送最后的ACK以防止ACK丢失(另一端超时并重发最后的FIN)。这就意味着:这个TCP连接在2MSL等待期间,与之对应的socket(客户端IP地址和端口号、服务端的IP地址和端口号)不能再被使用,只能在2MSL结束后才能再被使用。同时,在连接处于2MSL等待时间内,任何因为网络延迟或其它原因迟到的报文段都将被丢弃。”

MSL时间,它指一段报文段最大生存时间(max segment lifetime)。如果一段报文段在网络活动了MSL时间,还没有被接收,则会被丢弃。RFC 793指出MSL为120秒。现在网络,实际上常用的值是30s,60s和120秒。假定一台web服务器负载 为1000reqs/s,那么120s的MSL则意味着需要维护240,000个socket资源。负载过高的服务器有可能会提示too many open files错误。

观察与分析

我请求了一个简单的xmlrpc.PHP,通过tcpdump和netstat截取了下面数据来做分析。

[root@aikaiyuan ~]# tcpdump -n port 80 and host 218.1.57.236
12:08:33.870775 IP 218.1.57.236.44190 > 222.73.211.215.80: S 369292559:369292559(0) win 16384
12:08:33.870793 IP 222.73.211.215.80 > 218.1.57.236.44190: S 1960600277:1960600277(0) ack 369292560 win 5840
12:08:33.879524 IP 218.1.57.236.44190 > 222.73.211.215.80: . ack 1 win 17280
12:08:33.879531 IP 218.1.57.236.44190 > 222.73.211.215.80: P 1:513(512) ack 1 win 17280
12:08:33.879550 IP 222.73.211.215.80 > 218.1.57.236.44190: . ack 513 win 54
12:08:34.059372 IP 222.73.211.215.80 > 218.1.57.236.44190: P 1:349(348) ack 513 win 54
12:08:34.326185 IP 218.1.57.236.44190 > 222.73.211.215.80: . ack 349 win 16932
12:08:44.058702 IP 222.73.211.215.80 > 218.1.57.236.44190: F 349:349(0) ack 513 win 54
12:08:44.090925 IP 218.1.57.236.44190 > 222.73.211.215.80: F 513:513(0) ack 349 win 16932
12:08:44.090937 IP 222.73.211.215.80 > 218.1.57.236.44190: . ack 514 win 54
12:08:44.090940 IP 218.1.57.236.44190 > 222.73.211.215.80: . ack 350 win 16932
11 packets captured
11 packets received by filter
0 packets dropped by kernel
  1. 前三行,客户端与服务器手次握手建立tcp连接。服务端连接状态变为SYN_RECV
  2. 第四行,客户端浏览器向服务端守护进程httpd发送http请求. 服务端连接状态为ESTABLISHED
  3. 第五行,服务端向浏览器确认收到http请求
  4. 第六行,服务端脚本运行结束后向客户端浏览器发送http响应。可以看出,与上一次时间间隔相对来说更久。这部分时间是等待php-cgi执行时间。
  5. 第七行,浏览器向服务端确认收到http响应
  6. 第八行,服务端主动关闭连接,这个时候,还只是半关闭。服务端连接状态为FIN-wait-1。这表明http服务,是由服务端httpd守护进程主动关闭连接的,客户端浏览器是被动关闭连接一方。
  7. 第九行,客户端也关闭连接,即连接完全关闭。服务端收到这个FIN,连接状态变为TIME_WAIT
  8. 最后二行,分别确认FIN。ACK 514是最后一个ACK。即便这样,这个时候,此连接资源并没有被释放。状态依然保持为TIME_WAIT。直到1分钟后,此连接资源才被释放。
[root@aikaiyuan ~]# head -1 r.netstat ; tail -1 r.netstat
12:08:44 tcp 0 0 ::ffff:222.73.211.215:80 ::ffff:218.1.57.236:44190 TIME_WAIT
12:09:44 tcp 0 0 ::ffff:222.73.211.215:80 ::ffff:218.1.57.236:44190 TIME_WAIT

通过netstat监测网络连接状态。此连接第一次出现TIME_WAIT与最后一次出现TIME_WAIT相差1分钟,也就是说,服务器的MSL为30s。另外,netstat监测的结果与tcpdump监测的结果相吻合。

总结

  1. http服务器,由服务端主动关闭连接。
  2. 进程通过close()系统内核调用关闭连接,还需要保持socket资源,直到2MSL时间结束才会被释放。
  3. http1.1默认使用keep-alive,能够更好地利用TIME_WAIT连接资源。
  4. 负载高的web应用,可以通过ulimit -n将open files设置得更高一些,避免too many open files错误
  5. 通过以下设置,能够提高TIME_WAIT的利用率。
echo 1 > /proc/sys/net/ipv4/tcp_tw_recycle
echo 1 > /proc/sys/net/ipv4/tcp_tw_reuse

延伸阅读

评论