简单利用PoolingHttpClientConnectionManager来优化HttpClient方法,避免httpclient导致的排队堆积从而引发java.net.SocketException: Network is unreachable (connect failed)
背景描述(无关问题,可以忽略):
之前因为公司数据库访问速度太慢,导致我所负责的模块查询效率也及低,所以对其数据库方面做了优化,因为sql已经没有太多的优化空间了,所以就对数据库表做了调整。优化过程主要是将几张旧表中合法有效的数据转移到一张新表上,这样避免了每次查询的排查数据是否合法,也省去了两表联查三表联查等降低查询效率的字段。
而问题就出在将旧表数据转移到新表上时,有很多断链需要处理掉。我首先想到的是用HttpClient直接逐个请求,返回200保存到新表中,非200则丢弃。就这样,问题来了:
问题描述
HttpClient连接没有做关闭操作,导致new的httpclient过多造成tomcat假死 or DB死锁。
解决思路
利用httpclient池化技术来保证请求的数量,并且及时关闭无效的请求。
实现过程,直接上代码了
帮助类
/**
* httpclient池工具类
* @Author: zhuyu
* @Date: Create in 18:40:09 2020年12月20日 0020
*/
public class HttpClientHelper {
static PoolingHttpClientConnectionManager clientPool = new PoolingHttpClientConnectionManager();
// httpclient请求超时配置
static RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(2000).setConnectTimeout(2000).build();
// 将需要配置的值都放到静态代码块中,类被加载时,静态代码块也会被加载
static {
// 最大连接请求数
clientPool.setMaxTotal(5);
// 最大路由连接数
clientPool.setDefaultMaxPerRoute(5);
}
// 通过getHttpclientPool方法来获取httpclient,避免了直接去手动new
public static CloseableHttpClient getHttpclientPool() {
return HttpClients.custom().setConnectionManager(clientPool).setDefaultRequestConfig(requestConfig).build();
}
}
功能实现类
class Test {
private static final int SUCCESS_CODE = 200;
private static final String Read_Timed_Out_ERROR = "Read timed out";
public static void main(String[] args) {
// 开始时间
long startTime = System.currentTimeMillis();
// 请求链接
String requestHref = "http://www.baidu.com";
// 从HttpClient连接池中获取httpclient
CloseableHttpClient httpClient = HttpClientHelper.getHttpclientPool();
int times = 0;
for (int i = 0; i < 50; i++) {
HttpGet httpGet = new HttpGet(requestHref);
try {
CloseableHttpResponse response = httpClient.execute(httpGet);
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode != SUCCESS_CODE) {
System.out.println("发现断链");
}
times++;
} catch (Exception e) {
if (Read_Timed_Out_ERROR.equals(e.getMessage())) {
System.out.println("连接超时");
} else {
System.out.println("e.getMessage() = " + e.getMessage());
}
}finally {
httpGet.releaseConnection();
}
}
System.out.println("本次请求一共耗时:{" + (System.currentTimeMillis() - startTime) + "}"+"共请求次数 :"+times);
}
}
最后测试,大功告成。需要注意的是:
实现过程的bug
利用单例模式创建httpclient pool后,直接用httpclienthelper创建了httpclient,然后在关闭时遇到了问题,因为之前设置的最大连接数是5,
所以导致httpclient访问5次后就卡住了,说明请求没有被释放。
尝试直接close httpclient,但这样会导致Connection pool shut down
最终只能把希望寄托于httpGet上了,进入httpGet中寻找方法,果不其然!!
CloseableHttpResponse是可以刷新请求内部状态的,这样既可以使得pool中的五个连接继续发起请求,又可以避免new太多httpclient。
结语:一般如果要重复使用httpclient时,推荐使用连接池去操作,但如果只是请求几次,那么直接new也没什么关系,不过要记得close