安卓手机网站源码下载,机关单位网站建设申请,怎样在国外网站购买新鲜橙花做纯露,wordpress如何设置logo你好#xff01;我是miniluo#xff0c;今天和你分享使用HttpClient过程中#xff0c;未考虑释放连接和并发导致的坑。HttpClient在项目中还是比较常见的#xff0c;主要都是通过GET或POST请求第三方以获取响应结果。前段时间还了解到也有企业用它来做爬虫。下面我们就从两…你好我是miniluo今天和你分享使用HttpClient过程中未考虑释放连接和并发导致的坑。HttpClient在项目中还是比较常见的主要都是通过GET或POST请求第三方以获取响应结果。前段时间还了解到也有企业用它来做爬虫。下面我们就从两方面来一起学习HttpClient。强占着不放我们先来看一张因未释放连接导致的异常图。我们再来看代码代码很简单用一个static修饰HttpClient的一个对象也是用static的方式实例化(并发下static实例化的对象是线程不安全的)。1Slf4j2public class MyHttpClientTest{3 private static HttpClient client;4 static {5 RequestConfig requestConfig RequestConfig.custom()6 .setConnectTimeout(5000)7 .setConnectionRequestTimeout(3000)8 .setSocketTimeout(5000).build();9 client HttpClientBuilder.create().setDefaultRequestConfig(requestConfig)10 .build();11 }1213 Test14 public void testHttpClient(){15 for (int i 0; i 16 String url http://127.0.0.1:9092/learn/product/get/12;17 HttpGet httpGet new HttpGet(url);18 try {19 HttpResponse res client.execute(httpGet);20 } catch (IOException e) {21 log.error(异常 ,e);22 return;23 }24 }25 }26}我们看回异常图图中有2个红框我们先来看第一个红框的“[total kept alive: 0; route allocated: 2 of2; total allocated: 2 of 20]”这个DEBUG级别日志描述的什么意思呢也就是说这个route下共有2个连接已用2个pool共有20个已用2个。再来看第二个红框“Timeout waiting for connection from pool”从连接池获取连接等待超时。奇怪吧这顺序执行也会出现这其实是前两个连接响应结果回来后并没有释放连接资源导致后面的请求等待超时。我们看看PoolingHttpClientConnectionManager类的构造函数其给Pool初始化时给maxConnPerRoute和maxConnTotal设置了默认值。1 public PoolingHttpClientConnectionManager(2 final HttpClientConnectionOperator httpClientConnectionOperator,3 final HttpConnectionFactory connFactory,4 final long timeToLive, final TimeUnit tunit){5 super();6 this.configData new ConfigData();7 //defaultMaxPerRoute默认为2maxTotal默认为208 this.pool new CPool(new InternalConnectionFactory(9 this.configData, connFactory), 2, 20, timeToLive, tunit);10 this.pool.setValidateAfterInactivity(2000);11 this.connectionOperator Args.notNull(httpClientConnectionOperator, HttpClientConnectionOperator);12 this.isShutDown new AtomicBoolean(false);13 }所以当请求连接被占用2个后后面的请求并不会获取到连接这么说我们需要释放连接资源。这里我们需要介绍EntityUtils这个类里面包含了两个释放连接资源的方法consumeQuietly(HttpEntity entity)和consume(HttpEntity entity)前者内部也是请求后者完成关闭InputStream。调整后的代码如下1Test2 public void testHttpClient(){3 for (int i 0; i 4 String url http://127.0.0.1:9092/learn/product/get/12;5 HttpGet httpGet new HttpGet(url);6 HttpResponse res null;7 try {8 res client.execute(httpGet);9 } catch (IOException e) {10 log.error(异常 , e);11 return;12 } finally {13 if (null ! res) {14 EntityUtils.consumeQuietly(res.getEntity());15 }16 }17 }18 }调整完代码后我们执行后发现5个请求都能正确请求并得到响应结果。或许有同学会问到如果我调整maxConnPerRoute和maxConnTotal不也行吗可以但是你的连接还是没有释放当超过你设置值后也会出现无法获得连接池的问题。被CloseableHttpClient名字所坑最近项目中就是使用到CloseableHttpClient实现请求第三方踩完上面的坑后我们在项目中写了一个工厂类内部用枚举方式实现单例。经过并发测试后的确没有出现上述坑看了CloseableHttpClient有一个execute方法最后也有释放资源所以没有在意。直到今天写文章对应的测试案例我才发现我被这个方法欺骗了。CloseableHttpClient的execute()重载了多个方法而实际调用能释放资源的execute必须是包含ResponseHandler extends T这个属性否则和原有HttpClient方式是一样的。为何模拟高并发下并没有发生问题呢这是因为项目中请求响应结果后是用下面的代码完成数据获取的。1 HttpResponse response client.execute(get);2 String res EntityUtils.toString(response.getEntity());跟进EntityUtils.toString()方法的最底层调用会执行InputStream关闭流和上文说的consume方法一样。但是这里坑就坑在如果请求第三方超时或其他异常则直接跳过并没有执行上面的toString()方法那就不会释放连接资源。知道坑所在要解决也就好办。可以有下述两个方案1、外层调用增加finally释放连接上文所述。2、实例化一个ResponseHandler对象调用实现了释放连接资源的方法。总结今天我们一起学习了HttpClient和CloseableHttpClient没有正确释放连接以及并发受限的问题。实际项目中如果没有考虑到这两个情况则会带来生产问题后面的请求一直无法获取到连接资源以及并发量起不来。很多项目并不会有这个问题是因为HttpClient和CloseableHttpClient并不是单例的每次使用都会是新的实例。希望通过今天的学习对你日后使用HttpClient更加得心应手。由于篇幅问题没有把CloseableHttpClient案例放上来有兴趣的同学可以到GitHub上下载(https://github.com/littleluo/bj-share-java.git)。思考和讨论1、案例中我们使用了static和枚举的方式解决单例问题除了这两种方法外还有哪些方式呢他们各自的优缺点是什么2、说回前面关于分布式锁的文章中我们用到了redis也提到client.close()方式归还连接为何是归还连接而不是关闭连接呢欢迎留言与我分享和指正也欢迎你把这篇文章分享给你的朋友或同事一起交流。感谢您的阅读我们下节再见扫码关注我们与君共进