Windows Server 2008 R2上运行着一个服务A,它会从某地接收数据和图片,接受到的图片通过HTTP请求的形式提交给Tomcat上的应用B,应用B负责将图片存储到HBase中。由于服务A接收到的图片数量较多,并发较高。每次接受到一张图片,就创建一个线程向应用B提交数据,提交完成后,线程结束。
表现出来的问题当中,如下是最直接的三个问题:
还有一些衍生出来的问题,这些都是当时发现,但是不知道怎么回事,后来才通过理论推导归纳过来的:
问题出现的很诡异,图片丢失几分钟后,又不丢失图片了。当出现问题的时候,我重启服务A后,图片丢失的问题马上就解决了,但是运行一段时间之后,问题又会出现。
根据我的经验,先查看了一下远程连接8080端口的TCP连接数,可以到6万,直逼65535的极限。我感觉到是由于并发过高,端口占用过多,导致端口资源不够的问题。
针对这个假设我先在网上查了查Windows Server下端口回收的超时时间,给出的方法是修改注册表,增加KeepAliveTime等参数。还有说在Windows Server 上存在这个bug,需要安装两个补丁。我按照教程修改了注册表,安装了补丁,重启了服务器,发现效果依旧。
根据我的经验,我还检查了一下Tomcat所在的服务器上的8080端口连接情况,和服务器所有端口的使用数量。发现所有端口的使用数量和其他服务器上的使用数量相近,不存在该服务器端口占用过量的问题。观察8080端口的连接数量,也就在10~20之间,不存在由于连接过多造成Tomcat拒绝响应的问题。
在整个配查过程中,我心中一直存在一个疑问,我之前一直遇到的是TCP连接的TIME_WAIT状态,很少见到CLOSE_WAIT状态。这个疑问就让我比较关注CLOSE_WAIT这个名词。
后来我推测,可能是由于前端采集数量比较多,导致服务A的并发量比较高,我打算启用另一台Windows Server服务器来为服务A做负载均衡。
由于工程方面的原因,没有马上启用另一台服务器,我继续查找资料看能够解决这个问题。
我在查询资料的过程中,有几篇文章分别提到了TCP关闭过程的四次握手过程、Tomcat的keepalive机制,keepalive超时机制。
我在Windows Server上使用Wireshark抓包分析一段时间的网络流量。跟踪TCP流,找到服务A和应用B交互的TCP流,我发现在TCP流的末尾,没有Fin的过程。我还发现了只存在两个包的TCP流,第一个包是FIN包,第二个是ACK包。这个现象当时没有引起我的注意,在最后复盘的时候,才回忆起了这个现象,并解释了这个现象。
在我们的场景中,服务A是Client,应用B的Tomcat是Server。由于服务A应该是主动发起请求,主动关闭请求。所以按照服务A的角色,它的TCP连接不应该转入CLOSE_WAIT的状态。
然而事实确实如此,服务A端的TCP连接出现了CLOSE_WAIT状态。
那么做出一个假设,Tomcat端主动断开的连接,那么Tomcat主动断开连接的触发机会只有一个,那就是keepalive的超时机制。
后来我又进一步详细查阅了Tomcat的keepalive机制的资料,发现Tomcat默认启用了keepalive机制,并且默认存在一个超时设置。符合假设的所有前提。
当服务A启动一个线程,向Tomcat上的应用B发起请求后,发送相应的数据,由于在协议沟通过程中存在keepalive的机制,所以在数据发送完毕后,该连接并没有断开。此时由于数据发送任务完成,该进程也就销毁了,但是TCP连接依然存在。当Tomcat维持了一段时间的keepalive状态后,由于没有数据交互,触发了keepalive超时机制,Tomcat主动向Windows Server上发起了Fin包,请求终止连接,Windows Server立即响应Ack包,该TCP连接进入了CLOSE_WAIT的状态,前两次挥手完成。但是由于进程已经销毁,无法进行后两次的握手,所以这些TCP连接一直处在CLOSE_WAIT状态,一直累积,占用的TCP端口,直到将系统的端口资源耗光。
Tomcat发送Fin包,并得到ACK回应后,TCP连接进入FIN-WAIT-2状态,再等待2个MSL(Maximum Segment Lifetime,即报文最大生存时间)之后,进入CLOSED状态,所以Tomcat的服务器上没有出现TCP连接累积的情况。
我推测将Tomcat的keepalive机制关闭,服务A和Tomcat在协议协商过程中不使用keepalive,服务A在数据发送结束后应该会关闭连接。
Tomcat关闭长连接过程的配置为,修改server.xml文件:
其中设置maxKeepAliveRequests=“1”,也就是禁用了KeepAlive的功能。
修改了Tomcat的配置之后,重启Tomcat,重启服务A。
隔一段时间观察一次,发现连接处在了TIME_WAIT状态,仅有1~2个处在CLOSE_TIME状态,Windows Server上的总体连接数的数量也降下来了。
问题圆满解决。
翻回头去想,CLOSE_WAIT状态过多、TCP连接端口占用过多、其他应用的TCP连接也无法使用、Wireshark抓包出现的特殊形态的包等现象都能够解释的通了。
排查的问题的过程还比较曲折,修改过注册表参数、打过系统补丁,甚至想过去负载均衡,都非正解,但是排查问题就是这样,真正的解决方法是千呼万唤始出来。