解决:HttpClient导致应用出现过多Close_Wait的问题

最近发现一个问题,在服务器上通过netstat命令发现有大量的Close_Wait长时间存在,甚至有时候数量接近1000:

查看服务器参数(etc/sysctl.conf):

net.ipv4.tcp_keepalive_time 网管已经修改成1200。

参数值还可以改小,但似乎是治标不治本,出现这种问题,肯定是某个地方的程序本身存在问题。

根据ip及端口信息,不难发现是什么地方除问题了,项目中有涉及到图片上传,于是找到图片上传的代码,结果发现代码非常简单,一行上传权限初始化代码,一行CDN官方提供的一个静态方法,之后就是处理响应结果的代码了。代码少且简单,上传调用代码没什么问题,那么问题可能出在CDN官方提供的jar包了,好在CDN有提供源码,于是查看源码,源码中使用apache 的是httpClient包,调用代码大致如下:

        String response = "";
        HttpPost httpPost = null;
        CloseableHttpResponse ht = null;
        String startTime = formatter.format(new Date());//请求时间
        String endTime = "-";
        String statusCode = "-";
        String contentLength = "-";
        String contentType = "-";
        try {
            httpPost = new HttpPost(url);
            List<NameValuePair> paramsList = new ArrayList<NameValuePair>();

            if (file != null) {
                MultipartEntityBuilder mEntityBuilder = MultipartEntityBuilder.create();

                BandwithLimiterFileBody fileBody = new BandwithLimiterFileBody(file, null, "application/octet-stream", null, BaseBlockUtil.maxRate, progressNotifier);
                mEntityBuilder.addPart("file", fileBody);
                mEntityBuilder.addTextBody("desc", file.getName());

                if (params != null && params.size() > 0) {
                    for (String name : params.keySet()) {
                        mEntityBuilder.addTextBody(name, params.get(name), ContentType.create("text/plain", Charset.forName("UTF-8")));
                    }
                }

                httpPost.setEntity(mEntityBuilder.build());
            } else if (params != null && params.size() > 0) {
                for (String name : params.keySet()) {
                    paramsList.add(new BasicNameValuePair(name, params.get(name)));
                }
                HttpEntity he = new UrlEncodedFormEntity(paramsList, "utf-8");
                httpPost.setEntity(he);
            }

            if (headMap != null && headMap.size() > 0) {
                for (String name : headMap.keySet()) {
                    httpPost.addHeader(name, headMap.get(name));
                }
            }
            if(!httpPost.containsHeader("User-Agent"))
                httpPost.addHeader("User-Agent", Config.VERSION_NO);
            CloseableHttpClient hc = HttpClients.createDefault();
            RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(30000).setConnectTimeout(30000).build();//设置请求和传输超时时间
            httpPost.setConfig(requestConfig);
            ht = hc.execute(httpPost);
            endTime = formatter.format(new Date());

            Header[] headerArr = ht.getAllHeaders();
            for (Header header : headerArr) {
                BufferedHeader bh = (BufferedHeader) header;
                if (bh.getBuffer().toString().contains("Content-Length")) {
                    contentLength = bh.getValue();
                } else if (bh.getBuffer().toString().contains("Content-Type")) {
                    contentType = bh.getValue();
                }
            }
            HttpEntity het = ht.getEntity();
            InputStream is = het.getContent();
            BufferedReader br = new BufferedReader(new InputStreamReader(is, "utf8"));
            String readLine;
            while ((readLine = br.readLine()) != null) {
                response = response + readLine;
            }

            is.close();
            br.close();
            int status = ht.getStatusLine().getStatusCode();
            statusCode = String.valueOf(status);
            if (status == 200) {
                if (!new JsonValidator().validate(response)) {
                    response = EncodeUtils.urlsafeDecodeString(response);
                }
            }
            return new HttpClientResult(status, response);
        } catch (Exception e) {
            statusCode = "500";
            endTime = formatter.format(new Date());
            throw new HttpClientException(e);
        } finally {
            if (httpPost != null) {
                httpPost.releaseConnection();
            }
            if (ht != null) {
                try {
                    ht.close();
                } catch (IOException ignored) {
                }
            }
            writeHttpLog(startTime, url, "-", (null != params ? params.get("token") : "-"), (null != file ? file.getName() : "-"), "-", "-", endTime, statusCode, contentType, contentLength, response);
        }

查看TCP协议端口状态说明 , 如果一直保持在CLOSE_WAIT状态,那么只有一种情况,就是在对方关闭连接之后服务器程序自己没有进一步发出ack信号。因此要解决这个问题大致有以下几种方案:

a、实例化httpClient 时,使用alwaysClose 的SimpleHttpConnectionManager

  通常默认情况实例化httpClient 的时候使用的是无参构造的SimpleHttpConnectionManager,因此可替换为带参构造:

new HttpClient(new SimpleHttpConnectionManager(true));

b、在method.releaseConnection() 之后 通过获取HttpConnectionManager,进行关闭(getConnectionManager方法在httpclient 4.3之后已经标记为过期,后期可能会移除该方法):

hc.getConnectionManager().shutdown();

c、在method.releaseConnection() 之后 通过获取HttpConnectionManager 调用closeIdleConnections方法进行关闭,(getConnectionManager方法在httpclient 4.3之后已经标记为过期,后期可能会移除该方法):

hc.getConnectionManager().releaseConnection(conn, validDuration, timeUnit);

d、通过设置header由服务端自动关闭.

method.setHeader("Connection", "close"); 

HTTP协议中有关于这个属性的定义: 
HTTP/1.1 defines the "close" connection option for the sender to signal that the connection will be closed after completion of the response. For example:
      Connection: close

e、使用MultiThreadedHttpConnectionManager 进行复用,但同时也必须在合适的地方进行关闭处理;

f、请求之后未收到响应信息时,调用method.abort()进行处理

可参考:http://wiki.apache.org/HttpComponents/FrequentlyAskedConnectionManagementQuestions

    http://www.softlab.ntua.gr/facilities/documentation/unix/unix-socket-faq/unix-socket-faq-2.html#ss2.7

    TCP/IP详解卷一

时间: 2024-10-11 07:10:02

解决:HttpClient导致应用出现过多Close_Wait的问题的相关文章

解决VSCode导致电脑卡死的问题

解决VSCode导致电脑卡死的问题 文件-首选项-设置-搜索栏输入: search.followSymlinks:false git.enabled:false git.autorefresh:false 高级选择器:后代选择器.交集选择器.并集选择器.伪类选择器 1.后代选择器通过空格隔开 2.交集选择器通过.隔开 3.并集选择器通过,隔开 4.(1)静态伪类: 用于以下两个状态(只能使用于超链接): link:超链接点击之前 visited:超链接点击之后 (2)动态伪类: 用于以下几种状态

解决Oracle 11gR2 空闲连接过多,导致连接数满的问题

今天又遇到了11gR2连接数满的问题,以前也遇到过,因为应用那边没有深入检查,没有找到具体原因,暂且认为是这个版本Oracle的BUG吧. 上次的处理办法是用Shell脚本定时在系统中kill  v$session.status='INACTIVE'的连接,但是这次现场没有在操作系统中部署脚本的权限,只好在数据库中做处理,幸好我们对这个 数据库有完全的权限.这次使用了profile+JOB定时alter system kill 'sid,seral#' immediate的方式.具体脚本如下:

解决 HttpClient 模拟 http 的get 请求后 ,出现 403 错误

解决方法: URI uri = builder.build(); // 创建http GET请求 HttpGet httpGet = new HttpGet(uri); httpGet.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36"); //

解决httpclient抛出URISyntaxException异常

这两天在使用httpclient发送http请求的时候,发现url中一旦包含某些特殊字符就会报错.抛出URISyntaxException异常,比如struts漏洞的利用url:(大括号就不行) redirect:${%23req%3d%23context.get('com.opensymphony.xwork2.dispatcher.HttpServletRequest'),%23webroot%3d%23req.getSession().getServletContext().getReal

Spark使用CombineTextInputFormat缓解小文件过多导致Task数目过多的问题【转】

转自:http://www.cnblogs.com/yurunmiao/p/5195754.html 目前平台使用Kafka + Flume的方式进行实时数据接入,Kafka中的数据由业务方负责写入,这些数据一部分由Spark Streaming进行流式计算:另一部分数据则经由Flume存储至HDFS,用于数据挖掘或机器学习.HDFS存储数据时目录的最小逻辑单位为"小时",为了保证数据计算过程中的数据完整性(计算某个小时目录中的数据时,该目录的数据全部写入完毕,且不再变化),我们在Fl

解决SELinux导致Apache更改端口后无法启动的问题

systemctl start httpd    # 将Apache的默认端口改为90后,启动Apache时提示失败 systemctl status httpd    # 查看Apache的状态 可以看到提示:Permission denied: AH00072: make_sock: could not bind to address [::]:90,意思是说权限被拒绝:AH00072: make_sock:无法绑定到地址[::]:90 这是 SELinux 安全机制导致的 解决方法: se

git合并丢失代码问题分析与解决(错误操作导致)

问题描述 我们在主干dev和branch1分支上进行并行开发.当要把branch1功能的代码合并到dev上时,发现dev上开发的部分功能代码找不到了. 那么,是在branch1上,作了删除提交导致的吗?然而,查提交日志,并没有发现删代码的提交记录. 难道一个分支有一个功能,另一个分支没这个功能,git合并时就有可能把这块功能代码丢掉?跟功能添加时间顺序有关系? 为了解决这个问题和相关的疑问,我们需要先了解下git合并的过程. git-merge过程 稍微了解点git基础的应该都知道,合并是用的g

Unity中使用扩展方法解决foreach导致的GC

对于List这种顺序表,我们解决的时候还是可以使用for代替foreach即可.但是对于非顺序表,比如Dictionary或者Set之类,我们可以扩展方法Foreach,ForeachKey和ForeachValue来代替原有的foreach. 关于扩展方法,可参考:https://msdn.microsoft.com/zh-cn/library/bb383977.aspx static class DictionaryEx { /// <summary> /// 提供一个方法遍历所有项 //

LINUX下解决netstat查看TIME_WAIT状态过多问题(转)

原文连接:www.itokit.com/2012/0516/73950.html # netstat -an|awk '/tcp/ {print $6}'|sort|uniq -c 16 CLOSING     130 ESTABLISHED     298 FIN_WAIT1      13 FIN_WAIT2       9 LAST_ACK       7 LISTEN     103 SYN_RECV    5204 TIME_WAIT 状态:描述 CLOSED:无连接是活动的或正在进行