最近,公司的接口服务器(客户端,向外发送数据)频繁出现了connect timeout 以及readtime out 的情况,经过运维平台检测,并没有网络延时的情况。于是,开始怀疑连接池出了问题。

  使用linux命令: netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'  可以清楚的看到tcp各个状态下的连接数。

  

  如图: CLOSE_WAIT 数目大的惊人,问题就出在了这里:这个级别的TIME_WAIT是没有问题的, linux的句柄数(https://blog.csdn.net/shootyou/article/details/6579139)有限,大量的CLOSE_WAIT占去了过多的连接数,导致其他连接异常。

  据查:

  tcp连接三次握手,四次挥手。

  

    这其中,我们比较关注的状态有三个: ESTABLISHED 表示正在通信,TIME_WAIT 表示主动关闭,CLOSE_WAIT 表示被动关闭。

    其中:ESTABLISHED  无需多言,  TIME_WAIT  的存在是:

  1. 防止上一次连接中的包,迷路后重新出现,影响新连接(经过2MSL,上一次连接中所有的重复包都会消失)
  2. 可靠的关闭TCP连接。在主动关闭方发送的最后一个 ack(fin) ,有可能丢失,这时被动方会重新发fin, 如果这时主动方处于 CLOSED 状态 ,就会响应 rst 而不是 ack。所以主动方要处于 TIME_WAIT 状态,而不能是 CLOSED 。另外这么设计TIME_WAIT 会定时的回收资源,并不会占用很大资源的,除非短时间内接受大量请求或者受到攻击。

  

值 得一说的是,对于基于TCP的HTTP协议,关闭TCP连接的是Server端,这样,Server端会进入TIME_WAIT状态,可 想而知,对于访 问量大的Web Server,会存在大量的TIME_WAIT状态,假如server一秒钟接收1000个请求,那么就会积压 *=,000个 TIME_WAIT的记录,维护这些状态给Server带来负担。当然现代操作系统都会用快速的查找算法来管理这些 TIME_WAIT,所以对于新的 TCP连接请求,判断是否hit中一个TIME_WAIT不会太费时间,但是有这么多状态要维护总是不好。
HTTP协议1.1版规定default行为是Keep-Alive,也就是会重用TCP连接传输多个 request/response,一个主要原因就是发现了这个问题。

    也就是说HTTP的交互跟上面画的那个图是不一样的,关闭连接的不是客户端,而是服务器,所以web服务器也是会出现大量的TIME_WAIT的情况的。这也说清楚了,为什么我的客户端服务器会出现大量CLOSE_WAIT的情况。

    然后,我又在windows 端实时测试(netstat同样适用),的确,我所使用的HttpClient4 在每次接口发送完毕都会产生一个CLOSE_WAIT。这是为什么呢,使用连接池,是为了连接可以复用,而现在的情况确是相反的。

    经查,HttpClient4为了连接复用使用的都是长连接,Response的Header中默认Connection是Keep-alive即连接不会关闭,以便下次请求相同网站的时候进行复用,这是产生CLOSE_WAIT连接的原因所在。所以我猜测HttpClient4 故意留住CLOSE_WAIT

复用该连接。不会复用? 是不是我的对象创建的有问题?于是改了单例:

    

public class HttpApacheClient {

    private static class SingletonHandler{
private static HttpApacheClient singleton = new HttpApacheClient();
} private HttpApacheClient(){} public static HttpApacheClient getInstance(){
return SingletonHandler.singleton;
} private static Logger logger = LoggerFactory.getLogger(HttpApacheClient.class); private final HttpClientConnectionManager manager = builderPoolConnectionManager() ; //定义连接池管理变量
ResponseHandler<String> responseHandler = new ResponseHandler<String>() {
@Override
public String handleResponse(final HttpResponse response)
throws ClientProtocolException, IOException {
int status = response.getStatusLine().getStatusCode();
if (status >= 200 && status < 300) {
HttpEntity entity = response.getEntity();
if (entity == null) throw new BusinessException(RESULT_LOG_ERROR);
String content = EntityUtils.toString(entity);
logger.info("the log back :"+content);
JSONObject jsonObject = JSON.parseObject(content);
Integer returnStatus = (Integer)jsonObject.get("status");
if (returnStatus !=200) throw new BusinessException(RESULT_LOG_ERROR,content);
return content;
} else {
throw new ClientProtocolException("Unexpected response status: " + status);
}
}
}; public HttpClientConnectionManager builderPoolConnectionManager(){
final SSLContext context = SSLContexts.createSystemDefault();
final HostnameVerifier verifier = new DefaultHostnameVerifier() ;
//自定义注册器,既可以发送http请求,也可以发送https请求
final Registry<ConnectionSocketFactory> register = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http" , PlainConnectionSocketFactory.INSTANCE)
.register("https" , new SSLConnectionSocketFactory(context ,verifier))
.build() ;
PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager(register);
poolingHttpClientConnectionManager.setMaxTotal(200); //设置连接池的最大连接数
poolingHttpClientConnectionManager.setDefaultMaxPerRoute(poolingHttpClientConnectionManager.getMaxTotal()); //一个路由的最大连接数
return poolingHttpClientConnectionManager ;
} public HttpClient buildHttpClient(){
RequestConfig config = RequestConfig.custom()
.setConnectionRequestTimeout(3000) //从池中获取请求的时间
.setConnectTimeout(2000) //连接到服务器的时间
.setSocketTimeout(5000).build(); //读取信息时间 CloseableHttpClient build = HttpClients.custom()
.setRetryHandler(DefaultHttpRequestRetryHandler.INSTANCE)
.setDefaultRequestConfig(config)
.setConnectionManagerShared(true)
.setConnectionManager(manager)
.build();
return build ;
} public static String postJson(String url, List<NameValuePair> params) throws Exception {
String urlHead = (String) BeanHelper.getConfig("url");
url = urlHead+url;
logger.info("url "+url+";"+params);
HttpApacheClient utils = getInstance();
HttpClient httpClient = null;
HttpPost httpPost = null;
try {
httpClient = utils.buildHttpClient();
httpPost = new HttpPost(url);
httpPost.setEntity(new UrlEncodedFormEntity(params,"UTF-8"));
httpPost.setHeader("Content-type", "application/x-www-form-urlencoded");
return httpClient.execute(httpPost, utils.responseHandler);
}catch (Exception e){
if (httpPost!=null) httpPost.abort();//异常时 关闭连接
throw e;
}
} }
}

  问题得以解决。

  水比较深,未完待续。

HttpClient4 TIME_WAIT和CLOSE_WAIT的更多相关文章

  1. 服务器TIME_WAIT和CLOSE_WAIT详解和解决办法

    转载的服务器TIME_WAIT和CLOSE_WAIT详解和解决办法

  2. TCP连接(Time_Wait、Close_Wait)说明

    修改Time_Wait和CLOSE_WAIT时间 修改Time_Wait参数的方法 (在服务端修改)Windows下在HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlS ...

  3. TCP连接的TIME_WAIT和CLOSE_WAIT 状态解说【转】

    相信很多运维工程师遇到过这样一个情形: 用户反馈网站访问巨慢, 网络延迟等问题, 然后就迫切地登录服务器,终端输入命令"netstat -anp | grep TIME_WAIT | wc ...

  4. 一次TIME_WAIT和CLOSE_WAIT故障和解决办法

    昨天解决了一个curl调用错误导致的服务器异常,具体过程如下: 里头的分析过程有提到,通过查看服务器网络状态检测到服务器有大量的CLOSE_WAIT的状态. 在服务器的日常维护过程中,会经常用到下面的 ...

  5. TCP之 TIME_WAIT和CLOSE_WAIT 状态 的原因分析和处理

    转自:http://blog.csdn.net/shootyou/article/details/6622226 昨天解决了一个HttpClient调用错误导致的服务器异常,具体过程如下: http: ...

  6. 再谈应用环境下的TIME_WAIT和CLOSE_WAIT

    昨天解决了一个HttpClient调用错误导致的服务器异常,具体过程如下: http://blog.csdn.net/shootyou/article/details/6615051 里头的分析过程有 ...

  7. 谈应用环境下的TIME_WAIT和CLOSE_WAIT[转]

    昨天解决了一个HttpClient调用错误导致的服务器异常,具体过程如下: http://blog.csdn.net/shootyou/article/details/6615051 里头的分析过程有 ...

  8. 服务器大量TIME_WAIT和CLOSE_WAIT的原因及解决办法

    Linux服务器下查看网络连接的状态 netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}' 它会显示例如下面的信息: ...

  9. TIME_WAIT和CLOSE_WAIT状态区别

    [TIME_WAIT和CLOSE_WAIT状态区别] 常用的三个状态是:ESTABLISHED 表示正在通信,TIME_WAIT 表示主动关闭,CLOSE_WAIT 表示被动关闭. TCP协议规定,对 ...

随机推荐

  1. JavaScript脚本放在哪里

    在HTML body部分中的JavaScripts会在页面加载的时候被执行. 在HTML head部分中的JavaScripts会在被调用的时候才执行. ----------------------- ...

  2. JS核心笔记

    一.说明 JS权威指南文字用红色标出: JS高级程序设计用橙色标出; 自己加上的文字用粉红色标出: 其(一)-(九)为JS权指南,(十)为JS高级程序设计 二.记法结构 2.1字符集 Javascri ...

  3. 玩转Git入门篇

    最近项目使用到Git管理项目,所以就学习了一番,随然网上关于 Git的文章铺天盖地,我还是整理下总结下自己学习Git相关笔记,希望也能帮助到需要他的小伙伴们,O(∩_∩)O~ 简介 Git 是分布式版 ...

  4. 《你必须掌握的Entity Framework 6.x与Core 2.0》书籍出版

    前言 到目前为止写过刚好两百来篇博客,看过我博客的读者应该大概知道我每一篇博客都沿袭着一贯的套路,从前言到话题最终到总结,本文依然是一如既往的套路,但是不是介绍技术,也可说是介绍技术,不过是介绍书中的 ...

  5. com.android.dex.DexException: Multiple dex files define Lcom/sina/sso/RemoteSSO;

    错误原因:ShareSDK的包里面也包含微博SDK的代码,两个Jar包含重复. 解决方法:用Winrar到ShareSDK的Jar里面把sso目录删掉,编译即可成功

  6. C++判断字符串是否为空的一个小问题

    刚才visual studio下处理一个函数字符串入参,判断入参字符串是否为空有点小问题. 接口函数声明是这样的:SHORT GETWFSINFPINKEYDETAIL(LPCTSTR strKeyN ...

  7. Beta 冲刺day3

    1.昨天的困难,今天解决的进度,以及明天要做的事情 昨天的困难:昨天主要是对第三方与企业复杂的逻辑关系进行分析和优化,以及进行部分模块的功能测试和代码测试. 今天解决的进度:根据前天得到的需求问题进行 ...

  8. 使用ssh keys实现免验证登陆远程服务

    使用ssh keys实现免验证登陆远程服务========================Created 星期四 10 五月 2018 引言------------------程序员或者服务器运维人员 ...

  9. PAT1021:Deepest Root

    1021. Deepest Root (25) 时间限制 1500 ms 内存限制 65536 kB 代码长度限制 16000 B 判题程序 Standard 作者 CHEN, Yue A graph ...

  10. Linux修改本机/etc/hosts的hostName

    1.Linux修改本机别名/etc/hosts的hostName后经常不生效解决 Linux修改本机别名/etc/hosts的hostName后经常不生效, 比如我们/etc/hosts的内容如下: ...