序
這里簡單解釋一下httpclient一些關(guān)鍵參數(shù)的配置
超時時間
final RequestConfig requestConfig = RequestConfig.custom()
.setSocketTimeout(SOCKET_TIMEOUT)
.setConnectTimeout(CONNECTION_TIMEOUT)
.setCookieSpec(CookieSpecs.IGNORE_COOKIES).build();
這里設(shè)置了socket_timeout以及connection_timeout
KeepAliveStrategy
httpclient默認(rèn)提供了一個策略
httpclient-4.5.3-sources.jar!/org/apache/http/impl/client/DefaultConnectionKeepAliveStrategy.java
@Contract(threading = ThreadingBehavior.IMMUTABLE)
public class DefaultConnectionKeepAliveStrategy implements ConnectionKeepAliveStrategy {
public static final DefaultConnectionKeepAliveStrategy INSTANCE = new DefaultConnectionKeepAliveStrategy();
@Override
public long getKeepAliveDuration(final HttpResponse response, final HttpContext context) {
Args.notNull(response, "HTTP response");
final HeaderElementIterator it = new BasicHeaderElementIterator(
response.headerIterator(HTTP.CONN_KEEP_ALIVE));
while (it.hasNext()) {
final HeaderElement he = it.nextElement();
final String param = he.getName();
final String value = he.getValue();
if (value != null && param.equalsIgnoreCase("timeout")) {
try {
return Long.parseLong(value) * 1000;
} catch(final NumberFormatException ignore) {
}
}
}
return -1;
}
}
默認(rèn)的話,是從response里頭讀timeout參數(shù)的戏售,沒有讀到則設(shè)置為-1壁却,這個代表無窮躏鱼,這樣設(shè)置是有點(diǎn)問題了,如果是https鏈接的話监右,則可能會經(jīng)常報
Caused by: java.net.SocketTimeoutException: Read timed out
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
at java.net.SocketInputStream.read(SocketInputStream.java:170)
at java.net.SocketInputStream.read(SocketInputStream.java:141)
at sun.security.ssl.InputRecord.readFully(InputRecord.java:465)
at sun.security.ssl.InputRecord.read(InputRecord.java:503)
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:973)
at sun.security.ssl.SSLSocketImpl.readDataRecord(SSLSocketImpl.java:930)
at sun.security.ssl.AppInputStream.read(AppInputStream.java:105)
HTTP規(guī)范沒有確定一個持久連接可能或應(yīng)該保持活動多長時間埂伦。一些HTTP服務(wù)器使用非標(biāo)準(zhǔn)的頭部信息Keep-Alive來告訴客戶端它們想在服務(wù)器端保持連接活動的周期秒數(shù)顿涣。如果這個信息可用漩符,HttClient就會利用這個它一喘。如果頭部信息Keep-Alive在響應(yīng)中不存在,HttpClient假設(shè)連接無限期的保持活動嗜暴。然而許多現(xiàn)實(shí)中的HTTP服務(wù)器配置了在特定不活動周期之后丟掉持久連接來保存系統(tǒng)資源凸克,往往這是不通知客戶端的。
這里可以在此基礎(chǔ)上重寫一個闷沥,這里設(shè)置為5秒
ConnectionKeepAliveStrategy keepAliveStrategy = new DefaultConnectionKeepAliveStrategy() {
@Override
public long getKeepAliveDuration(final HttpResponse response, final HttpContext context) {
long keepAlive = super.getKeepAliveDuration(response, context);
if (keepAlive == -1) {
keepAlive = 5000;
}
return keepAlive;
}
};
Connection eviction policy
一個經(jīng)典的阻塞 I/O 模型的主要缺點(diǎn)是網(wǎng)絡(luò)套接字僅當(dāng) I/O 操作阻塞時才可以響應(yīng) I/O 事件萎战。當(dāng)一個連接被釋放返回管理器時,它可以被保持活動狀態(tài)而卻不能監(jiān)控套接字的狀態(tài)和響應(yīng)任何 I/O 事件狐赡。如果連接在服務(wù)器端關(guān)閉撞鹉,那么客戶端連接也不能去偵測連接狀態(tài)中的變化和關(guān)閉本端的套接字去作出適當(dāng)響應(yīng)慎框。
HttpClient 通過測試連接是否是過時的來嘗試去減輕這個問題哩簿,這已經(jīng)不再有效了丹皱,因?yàn)樗呀?jīng)在服務(wù)器端關(guān)閉了,之前使用執(zhí)行 HTTP 請求的連接览祖。過時的連接檢查也并不是 100%
的穩(wěn)定,反而對每次請求執(zhí)行還要增加10到30毫秒的開銷炊琉。唯一可行的而不涉及到每個對空閑連接的套接字模型線程解決方案展蒂,是使用專用的監(jiān)控線程來收回因?yàn)殚L時間不活動而被認(rèn)為是過期的連接。監(jiān)控線程可以周期地調(diào)用 ClientConnectionManager#closeExpiredConnections()方法來關(guān)閉所有過期的連接苔咪,從連接池中收回關(guān)閉的連接锰悼。它也可以選擇性調(diào)用ClientConnectionManager#closeIdleConnections()方法來關(guān)閉所有已經(jīng)空 閑超過給定時間周期的連接。
官方提供了一個實(shí)例
public static class IdleConnectionMonitorThread extends Thread {
private final HttpClientConnectionManager connMgr;
private volatile boolean shutdown;
public IdleConnectionMonitorThread(HttpClientConnectionManager connMgr) {
super();
this.connMgr = connMgr;
}
@Override
public void run() {
try {
while (!shutdown) {
synchronized (this) {
wait(5000);
// Close expired connections
connMgr.closeExpiredConnections();
// Optionally, close connections
// that have been idle longer than 30 sec
connMgr.closeIdleConnections(30, TimeUnit.SECONDS);
}
}
} catch (InterruptedException ex) {
// terminate
}
}
public void shutdown() {
shutdown = true;
synchronized (this) {
notifyAll();
}
}
}
在spring cloud netflix zuul中則提供了類似的定時器团赏,只不過參數(shù)是寫死的
spring-cloud-netflix-core/1.2.6.RELEASE/spring-cloud-netflix-core-1.2.6.RELEASE-sources.jar!/org/springframework/cloud/netflix/zuul/filters/route/SimpleHostRoutingFilter.java
private final Timer connectionManagerTimer = new Timer(
"SimpleHostRoutingFilter.connectionManagerTimer", true);
@PostConstruct
private void initialize() {
this.httpClient = newClient();
SOCKET_TIMEOUT.addCallback(this.clientloader);
CONNECTION_TIMEOUT.addCallback(this.clientloader);
this.connectionManagerTimer.schedule(new TimerTask() {
@Override
public void run() {
if (SimpleHostRoutingFilter.this.connectionManager == null) {
return;
}
SimpleHostRoutingFilter.this.connectionManager.closeExpiredConnections();
}
}, 30000, 5000);
}
每30秒清理一次失效的connection箕般,但是這里的closeExpiredConnections是依賴connTimeToLive參數(shù)以及keep alive strategy設(shè)置的。
httpcore-4.4.6-sources.jar!/org/apache/http/pool/AbstractConnPool.java
/**
* Closes expired connections and evicts them from the pool.
*/
public void closeExpired() {
final long now = System.currentTimeMillis();
enumAvailable(new PoolEntryCallback<T, C>() {
@Override
public void process(final PoolEntry<T, C> entry) {
if (entry.isExpired(now)) {
entry.close();
}
}
});
}
httpcore-4.4.6-sources.jar!/org/apache/http/pool/PoolEntry.java
public synchronized boolean isExpired(final long now) {
return now >= this.expiry;
}
public PoolEntry(final String id, final T route, final C conn,
final long timeToLive, final TimeUnit tunit) {
super();
Args.notNull(route, "Route");
Args.notNull(conn, "Connection");
Args.notNull(tunit, "Time unit");
this.id = id;
this.route = route;
this.conn = conn;
this.created = System.currentTimeMillis();
this.updated = this.created;
if (timeToLive > 0) {
this.validityDeadline = this.created + tunit.toMillis(timeToLive);
} else {
this.validityDeadline = Long.MAX_VALUE;
}
this.expiry = this.validityDeadline;
}
public synchronized void updateExpiry(final long time, final TimeUnit tunit) {
Args.notNull(tunit, "Time unit");
this.updated = System.currentTimeMillis();
final long newExpiry;
if (time > 0) {
newExpiry = this.updated + tunit.toMillis(time);
} else {
newExpiry = Long.MAX_VALUE;
}
this.expiry = Math.min(newExpiry, this.validityDeadline);
}
如果是-1舔清,則這里expiry為無窮大丝里。那么closeExpiredConnections其實(shí)是無效的。但是還依賴一個keep alive strategy体谒,它會去設(shè)置updateExpiry
httpclient-4.5.3-sources.jar!/org/apache/http/impl/execchain/MainClientExec.java
@Override
public CloseableHttpResponse execute(
final HttpRoute route,
final HttpRequestWrapper request,
final HttpClientContext context,
final HttpExecutionAware execAware) throws IOException, HttpException {
Args.notNull(route, "HTTP route");
Args.notNull(request, "HTTP request");
Args.notNull(context, "HTTP context");
AuthState targetAuthState = context.getTargetAuthState();
if (targetAuthState == null) {
targetAuthState = new AuthState();
context.setAttribute(HttpClientContext.TARGET_AUTH_STATE, targetAuthState);
}
AuthState proxyAuthState = context.getProxyAuthState();
if (proxyAuthState == null) {
proxyAuthState = new AuthState();
context.setAttribute(HttpClientContext.PROXY_AUTH_STATE, proxyAuthState);
}
if (request instanceof HttpEntityEnclosingRequest) {
RequestEntityProxy.enhance((HttpEntityEnclosingRequest) request);
}
Object userToken = context.getUserToken();
final ConnectionRequest connRequest = connManager.requestConnection(route, userToken);
if (execAware != null) {
if (execAware.isAborted()) {
connRequest.cancel();
throw new RequestAbortedException("Request aborted");
} else {
execAware.setCancellable(connRequest);
}
}
final RequestConfig config = context.getRequestConfig();
final HttpClientConnection managedConn;
try {
final int timeout = config.getConnectionRequestTimeout();
managedConn = connRequest.get(timeout > 0 ? timeout : 0, TimeUnit.MILLISECONDS);
} catch(final InterruptedException interrupted) {
Thread.currentThread().interrupt();
throw new RequestAbortedException("Request aborted", interrupted);
} catch(final ExecutionException ex) {
Throwable cause = ex.getCause();
if (cause == null) {
cause = ex;
}
throw new RequestAbortedException("Request execution failed", cause);
}
context.setAttribute(HttpCoreContext.HTTP_CONNECTION, managedConn);
if (config.isStaleConnectionCheckEnabled()) {
// validate connection
if (managedConn.isOpen()) {
this.log.debug("Stale connection check");
if (managedConn.isStale()) {
this.log.debug("Stale connection detected");
managedConn.close();
}
}
}
final ConnectionHolder connHolder = new ConnectionHolder(this.log, this.connManager, managedConn);
try {
if (execAware != null) {
execAware.setCancellable(connHolder);
}
HttpResponse response;
for (int execCount = 1;; execCount++) {
if (execCount > 1 && !RequestEntityProxy.isRepeatable(request)) {
throw new NonRepeatableRequestException("Cannot retry request " +
"with a non-repeatable request entity.");
}
if (execAware != null && execAware.isAborted()) {
throw new RequestAbortedException("Request aborted");
}
if (!managedConn.isOpen()) {
this.log.debug("Opening connection " + route);
try {
establishRoute(proxyAuthState, managedConn, route, request, context);
} catch (final TunnelRefusedException ex) {
if (this.log.isDebugEnabled()) {
this.log.debug(ex.getMessage());
}
response = ex.getResponse();
break;
}
}
final int timeout = config.getSocketTimeout();
if (timeout >= 0) {
managedConn.setSocketTimeout(timeout);
}
if (execAware != null && execAware.isAborted()) {
throw new RequestAbortedException("Request aborted");
}
if (this.log.isDebugEnabled()) {
this.log.debug("Executing request " + request.getRequestLine());
}
if (!request.containsHeader(AUTH.WWW_AUTH_RESP)) {
if (this.log.isDebugEnabled()) {
this.log.debug("Target auth state: " + targetAuthState.getState());
}
this.authenticator.generateAuthResponse(request, targetAuthState, context);
}
if (!request.containsHeader(AUTH.PROXY_AUTH_RESP) && !route.isTunnelled()) {
if (this.log.isDebugEnabled()) {
this.log.debug("Proxy auth state: " + proxyAuthState.getState());
}
this.authenticator.generateAuthResponse(request, proxyAuthState, context);
}
response = requestExecutor.execute(request, managedConn, context);
// The connection is in or can be brought to a re-usable state.
if (reuseStrategy.keepAlive(response, context)) {
// Set the idle duration of this connection
// 這里從keep alive strategy里頭讀取duration
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);
}
//這里update valid
connHolder.setValidFor(duration, TimeUnit.MILLISECONDS);
connHolder.markReusable();
} else {
connHolder.markNonReusable();
}
if (needAuthentication(
targetAuthState, proxyAuthState, route, response, context)) {
// Make sure the response body is fully consumed, if present
final HttpEntity entity = response.getEntity();
if (connHolder.isReusable()) {
EntityUtils.consume(entity);
} else {
managedConn.close();
if (proxyAuthState.getState() == AuthProtocolState.SUCCESS
&& proxyAuthState.getAuthScheme() != null
&& proxyAuthState.getAuthScheme().isConnectionBased()) {
this.log.debug("Resetting proxy auth state");
proxyAuthState.reset();
}
if (targetAuthState.getState() == AuthProtocolState.SUCCESS
&& targetAuthState.getAuthScheme() != null
&& targetAuthState.getAuthScheme().isConnectionBased()) {
this.log.debug("Resetting target auth state");
targetAuthState.reset();
}
}
// discard previous auth headers
final HttpRequest original = request.getOriginal();
if (!original.containsHeader(AUTH.WWW_AUTH_RESP)) {
request.removeHeaders(AUTH.WWW_AUTH_RESP);
}
if (!original.containsHeader(AUTH.PROXY_AUTH_RESP)) {
request.removeHeaders(AUTH.PROXY_AUTH_RESP);
}
} else {
break;
}
}
if (userToken == null) {
userToken = userTokenHandler.getUserToken(context);
context.setAttribute(HttpClientContext.USER_TOKEN, userToken);
}
if (userToken != null) {
connHolder.setState(userToken);
}
// check for entity, release connection if possible
final HttpEntity entity = response.getEntity();
if (entity == null || !entity.isStreaming()) {
// connection not needed and (assumed to be) in re-usable state
connHolder.releaseConnection();
return new HttpResponseProxy(response, null);
} else {
return new HttpResponseProxy(response, connHolder);
}
} catch (final ConnectionShutdownException ex) {
final InterruptedIOException ioex = new InterruptedIOException(
"Connection has been shut down");
ioex.initCause(ex);
throw ioex;
} catch (final HttpException ex) {
connHolder.abortConnection();
throw ex;
} catch (final IOException ex) {
connHolder.abortConnection();
throw ex;
} catch (final RuntimeException ex) {
connHolder.abortConnection();
throw ex;
}
}
主要看
httpclient-4.5.3-sources.jar!/org/apache/http/impl/execchain/ConnectionHolder.java
public void setValidFor(final long duration, final TimeUnit tunit) {
synchronized (this.managedConn) {
this.validDuration = duration;
this.tunit = tunit;
}
}
private void releaseConnection(final boolean reusable) {
if (this.released.compareAndSet(false, true)) {
synchronized (this.managedConn) {
if (reusable) {
this.manager.releaseConnection(this.managedConn,
this.state, this.validDuration, this.tunit);
} else {
try {
this.managedConn.close();
log.debug("Connection discarded");
} catch (final IOException ex) {
if (this.log.isDebugEnabled()) {
this.log.debug(ex.getMessage(), ex);
}
} finally {
this.manager.releaseConnection(
this.managedConn, null, 0, TimeUnit.MILLISECONDS);
}
}
}
}
}
這里設(shè)置了validDuration杯聚,會傳給releaseConnection
httpclient-4.5.3-sources.jar!/org/apache/http/impl/conn/PoolingHttpClientConnectionManager.java
public void releaseConnection(
final HttpClientConnection managedConn,
final Object state,
final long keepalive, final TimeUnit tunit) {
Args.notNull(managedConn, "Managed connection");
synchronized (managedConn) {
final CPoolEntry entry = CPoolProxy.detach(managedConn);
if (entry == null) {
return;
}
final ManagedHttpClientConnection conn = entry.getConnection();
try {
if (conn.isOpen()) {
final TimeUnit effectiveUnit = tunit != null ? tunit : TimeUnit.MILLISECONDS;
entry.setState(state);
entry.updateExpiry(keepalive, effectiveUnit);
if (this.log.isDebugEnabled()) {
final String s;
if (keepalive > 0) {
s = "for " + (double) effectiveUnit.toMillis(keepalive) / 1000 + " seconds";
} else {
s = "indefinitely";
}
this.log.debug("Connection " + format(entry) + " can be kept alive " + s);
}
}
} finally {
this.pool.release(entry, conn.isOpen() && entry.isRouteComplete());
if (this.log.isDebugEnabled()) {
this.log.debug("Connection released: " + format(entry) + formatStats(entry.getRoute()));
}
}
}
}
這里去updateExpiry,相當(dāng)于更新了timeToLive
connTimeToLive
這里設(shè)置的是http連接池中connection的存活時間
httpclient-4.5.3-sources.jar!/org/apache/http/impl/client/HttpClientBuilder.java
/**
* Sets maximum time to live for persistent connections
* <p>
* Please note this value can be overridden by the {@link #setConnectionManager(
* org.apache.http.conn.HttpClientConnectionManager)} method.
* </p>
*
* @since 4.4
*/
public final HttpClientBuilder setConnectionTimeToLive(final long connTimeToLive, final TimeUnit connTimeToLiveTimeUnit) {
this.connTimeToLive = connTimeToLive;
this.connTimeToLiveTimeUnit = connTimeToLiveTimeUnit;
return this;
}
httpclient-4.5.3-sources.jar!/org/apache/http/impl/conn/PoolingHttpClientConnectionManager.java
public PoolingHttpClientConnectionManager(
final Registry<ConnectionSocketFactory> socketFactoryRegistry,
final HttpConnectionFactory<HttpRoute, ManagedHttpClientConnection> connFactory,
final DnsResolver dnsResolver) {
this(socketFactoryRegistry, connFactory, null, dnsResolver, -1, TimeUnit.MILLISECONDS);
}
public PoolingHttpClientConnectionManager(
final Registry<ConnectionSocketFactory> socketFactoryRegistry,
final HttpConnectionFactory<HttpRoute, ManagedHttpClientConnection> connFactory,
final SchemePortResolver schemePortResolver,
final DnsResolver dnsResolver,
final long timeToLive, final TimeUnit tunit) {
this(
new DefaultHttpClientConnectionOperator(socketFactoryRegistry, schemePortResolver, dnsResolver),
connFactory,
timeToLive, tunit
);
}
默認(rèn)構(gòu)造的話抒痒,timeToLive是-1幌绍,如果默認(rèn)是-1的話,則不失效
maxTotal 以及 defaultMaxPerRoute
4.0的ThreadSafeClientConnManager,在4.2版本的時候被廢棄了傀广,默認(rèn)使用PoolingHttpClientConnectionManager颁独。這個manager有兩個重要的參數(shù),一個是maxTotal主儡,一個是defaultMaxPerRoute奖唯。
每個默認(rèn)的實(shí)現(xiàn)對每個給定路由將會創(chuàng)建不超過2個的并發(fā)連接,而總共也不會超過 20 個連接糜值。
this.pool = new CPool(new InternalConnectionFactory(this.configData, connFactory), 2, 20, timeToLive, tunit);
對于很多真實(shí)的應(yīng)用程序丰捷,這個限制也證明很大的制約,特別是他們在服務(wù)中使用 HTTP 作為 傳輸協(xié)議寂汇。連接限制病往,也可以使用 HTTP 參數(shù)來進(jìn)行調(diào)整。
spring cloud netflix zuul 里頭默認(rèn)配置是總共200連接骄瓣,每個route不超過20個連接
this.connectionManager = new PoolingHttpClientConnectionManager(registry);
this.connectionManager.setMaxTotal(this.hostProperties.getMaxTotalConnections()); // 200
this.connectionManager.setDefaultMaxPerRoute(this.hostProperties.getMaxPerRouteConnections()); //20
HttpRoute
httpclient-4.5.3-sources.jar!/org/apache/http/conn/routing/HttpRoute.java
HttpRoute對象是immutable的停巷,包含的數(shù)據(jù)有目標(biāo)主機(jī)、本地地址榕栏、代理鏈畔勤、是否tunnulled、是否layered扒磁、是否是安全路由庆揪。
@Contract(threading = ThreadingBehavior.IMMUTABLE)
public final class HttpRoute implements RouteInfo, Cloneable {
/** The target host to connect to. */
private final HttpHost targetHost;
/**
* The local address to connect from.
* {@code null} indicates that the default should be used.
*/
private final InetAddress localAddress;
/** The proxy servers, if any. Never null. */
private final List<HttpHost> proxyChain;
/** Whether the the route is tunnelled through the proxy. */
private final TunnelType tunnelled;
/** Whether the route is layered. */
private final LayerType layered;
/** Whether the route is (supposed to be) secure. */
private final boolean secure;
//...
}
IdleConnectionEvictor
httpclient-4.5.3-sources.jar!/org/apache/http/impl/client/IdleConnectionEvictor.java
public IdleConnectionEvictor(
final HttpClientConnectionManager connectionManager,
final ThreadFactory threadFactory,
final long sleepTime, final TimeUnit sleepTimeUnit,
final long maxIdleTime, final TimeUnit maxIdleTimeUnit) {
this.connectionManager = Args.notNull(connectionManager, "Connection manager");
this.threadFactory = threadFactory != null ? threadFactory : new DefaultThreadFactory();
this.sleepTimeMs = sleepTimeUnit != null ? sleepTimeUnit.toMillis(sleepTime) : sleepTime;
this.maxIdleTimeMs = maxIdleTimeUnit != null ? maxIdleTimeUnit.toMillis(maxIdleTime) : maxIdleTime;
this.thread = this.threadFactory.newThread(new Runnable() {
@Override
public void run() {
try {
while (!Thread.currentThread().isInterrupted()) {
Thread.sleep(sleepTimeMs);
connectionManager.closeExpiredConnections();
if (maxIdleTimeMs > 0) {
connectionManager.closeIdleConnections(maxIdleTimeMs, TimeUnit.MILLISECONDS);
}
}
} catch (final Exception ex) {
exception = ex;
}
}
});
}
builder默認(rèn)會構(gòu)造一個IdleConnectionEvictor
httpclient-4.5.3-sources.jar!/org/apache/http/impl/client/HttpClientBuilder.java
if (!this.connManagerShared) {
if (closeablesCopy == null) {
closeablesCopy = new ArrayList<Closeable>(1);
}
final HttpClientConnectionManager cm = connManagerCopy;
if (evictExpiredConnections || evictIdleConnections) {
final IdleConnectionEvictor connectionEvictor = new IdleConnectionEvictor(cm,
maxIdleTime > 0 ? maxIdleTime : 10, maxIdleTimeUnit != null ? maxIdleTimeUnit : TimeUnit.SECONDS);
closeablesCopy.add(new Closeable() {
@Override
public void close() throws IOException {
connectionEvictor.shutdown();
}
});
connectionEvictor.start();
}
closeablesCopy.add(new Closeable() {
@Override
public void close() throws IOException {
cm.shutdown();
}
});
}
如果沒有指定maxIdleTime的話,但是有設(shè)置evictExpiredConnections的話妨托,默認(rèn)是10秒
public final HttpClientBuilder evictExpiredConnections() {
evictExpiredConnections = true;
return this;
}
public final HttpClientBuilder evictIdleConnections(final long maxIdleTime, final TimeUnit maxIdleTimeUnit) {
this.evictIdleConnections = true;
this.maxIdleTime = maxIdleTime;
this.maxIdleTimeUnit = maxIdleTimeUnit;
return this;
}
connection request timeout
設(shè)置從連接池獲取一個連接的請求超時時間(連接池中連接不夠用的時候等待超時時間
)缸榛,單位毫秒,可以設(shè)置為500ms
httpclient-4.5.3-sources.jar!/org/apache/http/impl/execchain/MainClientExec.java
final RequestConfig config = context.getRequestConfig();
final HttpClientConnection managedConn;
try {
final int timeout = config.getConnectionRequestTimeout();
managedConn = connRequest.get(timeout > 0 ? timeout : 0, TimeUnit.MILLISECONDS);
} catch(final InterruptedException interrupted) {
Thread.currentThread().interrupt();
throw new RequestAbortedException("Request aborted", interrupted);
} catch(final ExecutionException ex) {
Throwable cause = ex.getCause();
if (cause == null) {
cause = ex;
}
throw new RequestAbortedException("Request execution failed", cause);
}
retry
設(shè)置重試策略
httpclient-4.5.3-sources.jar!/org/apache/http/impl/client/DefaultHttpRequestRetryHandler.java
public DefaultHttpRequestRetryHandler(final int retryCount, final boolean requestSentRetryEnabled) {
this(retryCount, requestSentRetryEnabled, Arrays.asList(
InterruptedIOException.class,
UnknownHostException.class,
ConnectException.class,
SSLException.class));
}
比如不重試兰伤,可以這樣設(shè)置
httpClientBuilder.setConnectionManager(newConnectionManager())
.useSystemProperties().setDefaultRequestConfig(requestConfig)
.setRetryHandler(new DefaultHttpRequestRetryHandler(0, false));
小結(jié)
當(dāng)getKeepAliveDuration為-1以及connTimeToLive為-1的時候内颗,closeExpiredConnections方法其實(shí)是沒有用的,查看debug日志會出現(xiàn)
o.a.http.impl.client.DefaultHttpClient 509 : Connection can be kept alive indefinitely
當(dāng)getKeepAliveDuration不為-1的話敦腔,假設(shè)是5s,則日志可能是這樣的
o.a.http.impl.execchain.MainClientExec 285 : Connection can be kept alive for 5000 MILLISECONDS