欢迎光临
我们一直在努力

服务器出现大量close_wait,我们来说说到底是怎么回事?(以tomcat为例)

一、问题描述

最近一直忙得很,好久没写博客。前两天,微信收到个好友申请,说是想问问close_wait的事情。

服务器出现大量close_wait,我们来说说到底是怎么回事?(以tomcat为例)

 

找他问了些详细信息,大概了解到,他们后端服务是tomcat 7, jdk 7,centos,传统的spring + hibernate + spring mvc 结构。

业务不清楚,客户端主要是微信小程序。

目前的症状就是,服务器上有大量的close_wait状态的连接,在他们的服务器上执行 netstat 命令,如下图:

服务器出现大量close_wait,我们来说说到底是怎么回事?(以tomcat为例)

 

从上图看出,他们的服务器ip为 172.18.206.252(反正是内网ip,不用打码了吧),端口是443,那应该就是https服务了。

客户端ip没有重样的,应该都是些全国各地的ip了。

close_wait的危害在于,在一个进程上打开的文件描述符超过一定数量,(在linux上默认是1024,可修改),新来的socket连接就无法建立了,因为每个socket连接也算是一个文件描述符。

会报:Too many open files。

这里详细说一下,linux上,每个进程,都有其自身的资源限制,比如最大可以打开的文件描述符数量。

比如下面,我查看了某个java进程的资源限制。

服务器出现大量close_wait,我们来说说到底是怎么回事?(以tomcat为例)

 

 这部分,可以查看以下链接:

https://feichashao.com/ulimit_demo/

 

二、问题分析

我回想了一下,之前在博客园上是写了一篇close_wait相关的,叫:tcp连接出现close_wait状态?可能是代码不够健壮

在那篇文章里,虽然我那也是服务端出现的,但是服务端其实是作为客户端,去调用大数据服务。严格来说,和今天遇到的场景不一样:

1、之前博客里的场景:服务端调用大数据,大数据关闭连接,(发起fin,服务器回ack)。此时,因为代码不严谨,服务端没有再向大数据发起close请求,

所以服务端与大数据的连接,在服务端上表现为close_wait。在大数据那边,状态应该是属于FIN_WAIT_2。参考下图:

服务器出现大量close_wait,我们来说说到底是怎么回事?(以tomcat为例)

 

 2、这次遇到的场景:是作为小程序的客户端访问了服务器,服务器不知道为啥处于close_wait。

所以,一开始我也没有思路,网上查了下,有人说是tomcat 的https有bug,更多的直接教你怎么用运维手段解决。

 

后来,这个朋友提到,他们服务器的一个接口,是会去调用微信服务器,生成二维码,而且,他们的监控显示,该方法耗时较长。

这时,我想到一个问题是:如果服务端在处理过程中,耗时较长,(进入死循环、等锁、下游服务响应慢等),假设20s才返回,

但是客户端明显不可能等那么久,一般5-10s就超时了。超时了,客户端发起fin,服务器回ack,此时,

服务器端应该就是close_wait。

 

在网上搜索时,也发现网上其实有这方面案例了,比如:

一次服务端大量CLOSE_WAIT问题的解决

 

我大概率估计,就是这个原因。但猜测只是猜测,还是要实践一下。

 

三、验证猜测

1、准备工作

我这边的一台开发服务器是windows的,装了wireshark,方便抓包,上面装了tomcat 8.5,一会直接把war包丢进去跑就完了。

我的打算是,修改目前工程的一个controller接口,让其睡眠30s再返回。 客户端的话,我用了httpclient,写了个测试类,直接去调用服务器的controller接口。

然后,用netstat观察该连接的状态变化,同时,wireshark辅助查看网络包的发送情况。

 

controller代码如下:

服务器出现大量close_wait,我们来说说到底是怎么回事?(以tomcat为例)

 

客户端代码:

pom.xml加上:

 <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.3</version> </dependency>

 

工具类如下:

import com.alibaba.fastjson.JSON; import com.ceiec.base.common.utilities.AppConstants; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.time.StopWatch; import org.apache.http.HttpEntity; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.message.BasicHeader; import org.apache.http.protocol.HTTP; import org.apache.http.util.EntityUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.util.HashMap; public final class MyHttpUtils { private static int TIMEOUT = 5000; private static final String APPLICATION_JSON = "application/json"; private static final String CONTENT_TYPE_TEXT_JSON = "text/json"; private static Logger logger = LoggerFactory.getLogger(MyHttpUtils.class); /** * POST方式提交请求 * * @param url 请求地址 * @param json JSON格式请求内容 * @throws IOException */ public static String doPost(String url, String json){ if (json == null) { HashMap<String, String> map = new HashMap<>(); json = JSON.toJSONString(map); } //计时 StopWatch timer = new StopWatch(); timer.start(); RequestConfig defaultRequestConfig = RequestConfig.custom().setSocketTimeout(TIMEOUT).setConnectTimeout(TIMEOUT).setConnectionRequestTimeout(TIMEOUT).build(); CloseableHttpClient httpClient = HttpClients.custom().setDefaultRequestConfig(defaultRequestConfig).build(); HttpPost httpPost = new HttpPost(url); httpPost.addHeader(HTTP.CONTENT_TYPE, APPLICATION_JSON); StringEntity stringEntity = new StringEntity(json, AppConstants.UTF8); stringEntity.setContentType(CONTENT_TYPE_TEXT_JSON); stringEntity.setContentEncoding(new BasicHeader(HTTP.CONTENT_TYPE, APPLICATION_JSON)); httpPost.setEntity(stringEntity); httpPost.setConfig(defaultRequestConfig); CloseableHttpResponse response = null; String responseContent = ""; try { response = httpClient.execute(httpPost); int status = response.getStatusLine().getStatusCode(); logger.debug("response status: " + status); if (status >= 200 && status < 300) { HttpEntity entity = response.getEntity(); if (entity == null) { return null; } responseContent = EntityUtils.toString(entity,"UTF-8"); return responseContent; } else { throw new ClientProtocolException("Unexpected response status: " + status); } } catch (Exception e) { logger.error("error occured.{}",e); throw new RuntimeException(e); } finally { timer.stop(); logger.info("doPost. requestUrl:{}, param:{},response:{},took {} ms", url,json,responseContent,timer.getTime()); IOUtils.closeQuietly(response); IOUtils.closeQuietly(httpClient); } } }

 

Test类:

public class Test { public static void main(String[] args) { MyHttpUtils.doPost("http://192.168.19.94:8080/CAD_WebService/getValue.do",null); } }

 

好了,在正式开始之前,说下MyHttpUtils中标红的那个方法:RequestConfig.custom().setSocketTimeout(TIMEOUT) 这个setSocketTimeout表示设置等待服务器端响应超时的时间,这里设为5000,意为5s

 

 

2.测试验证

这里,准备就绪了,马上开始,我们启动了服务端,然后客户端发起调用,下面是我这边的抓包(服务端视角):

服务器出现大量close_wait,我们来说说到底是怎么回事?(以tomcat为例)

 

这个图,咱们先看抓包:

这个包,是在服务器192.168.19.94上抓的。抓的是服务器上8080端口,和我本地pc 10.15.4.46之间的网络包。

我们分析下:

序号为1/2/3的包: 三次握手,建立连接;

序号4的包:发起http请求,请求的controller方法,会睡眠30s

序号5的包:对序号4的包的ack。注意,此时时间为14:02:11

序号6的包:此时时间已经过去5s,客户端等不及了,(就你猴急?),于是不耐烦了,老子不等了,发了个fin过来,要分手。

序号7的包:服务器说:要分手?知道了。

 

此时服务器在干嘛,不好意思,要睡30s,这时才睡了5s,还没醒。

 

说完了包,我们再看看那个cmd,里面展示的是8080端口上的连接,可以看出来,此时该连接正处于close_wait状态。

 

。。。

25s后。。。

服务器出现大量close_wait,我们来说说到底是怎么回事?(以tomcat为例)

25s后,服务器终于醒了,睡得不错,给客户端发响应吧(序号8),但是呢,9号包可以看出来,我的开发机给服务器回了rst。

意思就是:我不认识你。(因为前面我的开发机就提了分手。。。)

 

3.gif图完整回放

由于是一边写一边截图的,所以有些图等写完了再想看的时候,已经没有了。

 

下面截个完整的,这里,把客户端超时改为20s,方便查看:

1、服务端视角,可以看到,超时前,为established,超时后,为close_wait。

服务器出现大量close_wait,我们来说说到底是怎么回事?(以tomcat为例)

 

2.客户端视角

服务器出现大量close_wait,我们来说说到底是怎么回事?(以tomcat为例)

 

 

四、说到底,问题怎么解决

扯了那么多,服务器出现大量close_wait,到底怎么解决? 指标不治本的方法我就不说了,直接网上搜下,改改linux参数即可。

这篇博客主要是治本,讲了close_wait出现的一种情况:

服务端接口耗时较长,客户端主动断开了连接,此时,服务端就会出现 close_wait。

 

那怎么解决呢?看看代码为啥耗时长吧。

另外,如果代码不规范的话,说不定在收到对方发起的fin后,自己根本就不会给人家发fin。(比如netty自己开发的框架那种)

没啥好说的,检查自己的代码吧,反正close_wait基本就是自己这边的问题了。

如果觉得有点帮助,麻烦点个推荐哈。

 

ps:我这里用chrome测过,用fiddler的composer也搞过,发现有些客户端会一直等响应,过了很久才会主动去发fin,所以用了httpclient测试。

pps:tomcat在什么情况会主动发起fin?其实我也想讲讲,因为和http的connection:keep-alive这些,都有点关系,放这篇文章的话,主题就有点不集中,放下篇吧。

 

网络上,我也发现有些差不多场景的文章:

https://blog.csdn.net/auo67284/article/details/101102578

  • 海报
海报图正在生成中...
赞(0) 打赏
声明:
1、本博客不从事任何主机及服务器租赁业务,不参与任何交易,也绝非中介。博客内容仅记录博主个人感兴趣的服务器测评结果及一些服务器相关的优惠活动,信息均摘自网络或来自服务商主动提供;所以对本博客提及的内容不作直接、间接、法定、约定的保证,博客内容也不具备任何参考价值及引导作用,访问者需自行甄别。
2、访问本博客请务必遵守有关互联网的相关法律、规定与规则;不能利用本博客所提及的内容从事任何违法、违规操作;否则造成的一切后果由访问者自行承担。
3、未成年人及不能独立承担法律责任的个人及群体请勿访问本博客。
4、一旦您访问本博客,即表示您已经知晓并接受了以上声明通告。
文章名称:《服务器出现大量close_wait,我们来说说到底是怎么回事?(以tomcat为例)》
文章链接:https://www.456zj.com/5518.html
本站资源仅供个人学习交流,请于下载后24小时内删除,不允许用于商业用途,否则法律问题自行承担。

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址