HttpClient 的一次Timeout waiting fo
2018-12-07 本文已影响0人
IT菜鸟学习
设置了defaultMaxPerRoute为20。maxTotal设置为100.
/**
* 首先实例化一个连接池管理器,设置最大连接数、并发连接数
*
* @return
*/
@Bean(name = "httpClientConnectionManager")
public PoolingHttpClientConnectionManager getHttpClientConnectionManager() {
PoolingHttpClientConnectionManager httpClientConnectionManager = new PoolingHttpClientConnectionManager(60, TimeUnit.SECONDS);
//最大连接数
httpClientConnectionManager.setMaxTotal(maxTotal);
//并发数
httpClientConnectionManager.setDefaultMaxPerRoute(defaultMaxPerRoute);
// Validate connections after 1 sec of inactivity
httpClientConnectionManager.setValidateAfterInactivity(validateAfterInactivity);
return httpClientConnectionManager;
}
HttpUtil工具类,主要负责关闭rsp
public class HttpUtil {
private static Logger logger = LoggerFactory.getLogger(HttpUtil.class);
/**
* 转化为字符串
*
* @param resp 响应对象
* @param encoding 编码
* @return 返回处理结果
* @throws ServiceException http处理异常
*/
public static String formTString(HttpResponse resp, String encoding) throws ServiceException {
String body = "";
try {
if (resp.getEntity() != null) {
// 按指定编码转换结果实体为String类型
body = EntityUtils.toString(resp.getEntity(), encoding);
logger.info(body);
}else{//有可能是head请求
body =resp.getStatusLine().toString();
logger.info("返回实体为空NULL");
}
EntityUtils.consume(resp.getEntity());
} catch (IOException e) {
throw new ServiceException(ErrorCode.HTTP_ERR_DESC, ErrorCode.HTTP_ERR_CODE, "", "");
}finally{
close(resp);
}
return body;
}
/**
* 尝试关闭response
*
* @param resp HttpResponse对象
*/
private static void close(HttpResponse resp) {
try {
if(resp == null) {
return;
}
//如果CloseableHttpResponse 是resp的父类,则支持关闭
if(CloseableHttpResponse.class.isAssignableFrom(resp.getClass())){
((CloseableHttpResponse)resp).close();
}
} catch (IOException e) {
logger.error(e.getMessage());
}
}
}
封装的请求,这里当header的code为200的时候才会进入去执行result = HttpUtil.formTString(response, "UTF-8"); 这个方法中会关闭rsp。当code不等于200的时候,超过了20次就会抛出Timeout waiting for connection from pool错误。
/**
* 不带参数的get请求,如果状态码为200,则返回body,如果不为200,则返回null
*
* @param apiUrl 请求地址
* @return body
* @throws ServiceException ServiceException
*/
public static String aiGet(String accessToken, String deviceSerial, String apiUrl, Map<String, Object> params) throws ServiceException {
String result = null;
// 声明 http get 请求
HttpGet httpGet = new HttpGet(ISApiUrl.ISAPI_DOMAIN+apiUrl);
// 装载配置信息
httpGet.setConfig(serviceClient.config);
//获取头信息
Map<String, String> headers = getHeaders(accessToken, deviceSerial);
for (Map.Entry<String, String> e : headers.entrySet()) {
httpGet.addHeader(e.getKey(), e.getValue());
}
logger.info("请求HTTP_config:header==" + Arrays.toString(httpGet.getAllHeaders()) + "==map=");
CloseableHttpResponse response = null;
try {
// 发起请求
response = serviceClient.httpClient.execute(httpGet);
response.getFirstHeader("Code");
// 判断状态码是否为200
if (response.getStatusLine().getStatusCode() == 200) {
if (!"200".equals(response.getFirstHeader("Code").getValue())) {
throw new ServiceException(response.getFirstHeader("Message").getValue(), response.getFirstHeader("Code").getValue(), null, apiUrl);
}
// 返回响应体的内容
//result = EntityUtils.toString(response.getEntity(), "UTF-8");
result = HttpUtil.formTString(response, "UTF-8");
}
} catch (IOException e) {
logger.error(e.toString());
logger.error("Http异常",e);
throw new ServiceException(ErrorCode.HTTP_ERR_DESC, ErrorCode.HTTP_ERR_CODE, "", apiUrl);
}
return result;
}
这里
result = HttpUtil.formTString(response, "UTF-8");
linux运行:
netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
[root@10 ~]# netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
TIME_WAIT 4
CLOSE_WAIT 22
ESTABLISHED 100
发现CLOSE_WAIT的数量始终在20以上,一直没降过。
关于TIME_WAIT和CLOSE_WAIT的区别和异常处理可以自行查找。
简单来说CLOSE_WAIT数目过大是由于被动关闭连接处理不当导致的。
我说一个场景,服务器A会去请求服务器B上面的apache获取文件资源,正常情况下,如果请求成功,那么在抓取完资源后服务器A会主动发出关闭连接的请求,这个时候就是主动关闭连接,连接状态我们可以看到是TIME_WAIT。如果一旦发生异常呢?假设请求的资源服务器B上并不存在,那么这个时候就会由服务器B发出关闭连接的请求,服务器A就是被动的关闭了连接,如果服务器A被动关闭连接之后自己并没有释放连接,那就会造成CLOSE_WAIT的状态了。
改后正确的
public static String aiGet(String accessToken, String deviceSerial, String apiUrl, Map<String, Object> params) throws ServiceException {
String result = null;
// 声明 http get 请求
HttpGet httpGet = new HttpGet(ISApiUrl.ISAPI_DOMAIN+apiUrl);
// 装载配置信息
httpGet.setConfig(serviceClient.config);
//获取头信息
Map<String, String> headers = getHeaders(accessToken, deviceSerial);
for (Map.Entry<String, String> e : headers.entrySet()) {
httpGet.addHeader(e.getKey(), e.getValue());
}
logger.info("请求HTTP_config:header==" + Arrays.toString(httpGet.getAllHeaders()) + "==map=");
CloseableHttpResponse response = null;
try {
// 发起请求
response = serviceClient.httpClient.execute(httpGet);
result = HttpUtil.formTString(response, "UTF-8");
response.getFirstHeader("Code");
// 判断状态码是否为200
if (response.getStatusLine().getStatusCode() == 200) {
if (!"200".equals(response.getFirstHeader("Code").getValue())) {
throw new ServiceException(response.getFirstHeader("Message").getValue(), response.getFirstHeader("Code").getValue(), null, apiUrl);
}
// 返回响应体的内容
//result = EntityUtils.toString(response.getEntity(), "UTF-8");
}
} catch (IOException e) {
logger.error(e.toString());
logger.error("Http异常",e);
throw new ServiceException(ErrorCode.HTTP_ERR_DESC, ErrorCode.HTTP_ERR_CODE, "", apiUrl);
}
return result;
}
参考:https://my.oschina.net/fuxingCoder/blog/809835
https://blog.csdn.net/shootyou/article/details/6615051