根据Apache HttpClient 4.5.6讲解。
HttpClient使用如下方式创建HttpClient时,在build()中会默认给HttpClient设置一个连接池PoolingHttpClientConnectionManager
return HttpClientBuilder.create().build();
默认创建的HttpClient是默认保持连接的keepalive。
我们使用HttpClient请求第三方服务器时,如果请求的是同一个服务器的地址是可以提高访问效率的。这是由于HTTP协议的底层是TCP,TCP建立连接的时候需要三次握手。如果使用连接池并keeplive是true,那么就是一个长链接,在第一次握手成功后,后面的请求就都不需要握手了,直接请求数据,这样就加快了访问速度。
我们一般获取到返回结果后都会做一些资源关闭的操作,HttpClient的executor方法也会默认做response的关闭操作。比如:
CloseableHttpResponse response = this.execute(target, request, context);
....
finally {
response.close();
}
那有没有想过这样关闭会对我上面说的长连接有影响吗?
如果是长连接这样关闭是没有影响的,它还是在连接池中。close()最后会调用下面这个方法。在finally中的close传入的参数是false,也就是会释放掉链接,但是它并不会调用成功。因为在前面已经执行过一次releaseConnection方法了(releaseConnection方法只能调用一次)。在response放回处理返回的结果的时候会调用close方法。那里的参数使用的是ConnectionHolder类的reusable成员变量。所以如果前面的程序执行成功最后的finally中的close方法到releaseConnection方法的CAS时会执行失败。
private void releaseConnection(boolean reusable) {
if (this.released.compareAndSet(false, true)) {
HttpClientConnection var2 = this.managedConn;
synchronized(this.managedConn) {
if (reusable) {
this.manager.releaseConnection(this.managedConn, this.state, this.validDuration, this.tunit);
} else {
try {
this.managedConn.close();
this.log.debug("Connection discarded");
} catch (IOException var9) {
if (this.log.isDebugEnabled()) {
this.log.debug(var9.getMessage(), var9);
}
} finally {
this.manager.releaseConnection(this.managedConn, (Object)null, 0L, TimeUnit.MILLISECONDS);
}
}
}
}
}
可以看到是通过reusable去判断的。这个在CloseableHttpResponse构造的时候就已经设定了。
if (reuseStrategy.keepAlive(response, context)) {
// Set the idle duration of this connection
final long duration = keepAliveStrategy.getKeepAliveDuration(response, context);
if (this.log.isDebugEnabled()) {
final String s;
if (duration > 0) {
s = "for " + duration + " " + TimeUnit.MILLISECONDS;
} else {
s = "indefinitely";
}
this.log.debug("Connection can be kept alive " + s);
}
connHolder.setValidFor(duration, TimeUnit.MILLISECONDS);
connHolder.markReusable();
} else {
connHolder.markNonReusable();
}
------
public void markReusable() {
this.reusable = true;
}
----
public void markNonReusable() {
this.reusable = false;
}
看这个大概就可以看出,就是通过keepAlive去判断的。如果是需要保持长连接的就将reusable设定为true,否则会设定为false。
回到前面的releaseConnection(),它没有去关闭socket,而是将连接放入了一个链表供后面使用。然后再执行的时候去这个链表中获取连接。这个链表是RouteSpecificPool类available成员变量。获取连接使用的是getFree方法。
前面我们讨论了使用一个httpClient请求一个地址的时候可以使用长连接来优化请求速度。如果这个httpClent会被用来访问多个地址呢(一般都是会有一个专门的HTTP请求工具类)?
这个可以放心的使用,该有的长连接还是会继续有(没有超时的)。下次访问还是会用到这个长连接。这是因为HttpClient会根据不同的请求地址做不同的缓存。具体可以看org.apache.http.pool.AbstractConnPool的设计。
以上。