httpclient staleConnectionCheckEnabled獲取連接流程解析
序
本文主要研究一下httpclient的staleConnectionCheckEnabled
staleConnectionCheckEnabled
org/apache/http/client/config/RequestConfig.java
public class RequestConfig implements Cloneable { public static final RequestConfig DEFAULT = new Builder().build(); private final boolean expectContinueEnabled; private final HttpHost proxy; private final InetAddress localAddress; private final boolean staleConnectionCheckEnabled; //...... /** * Determines whether stale connection check is to be used. The stale * connection check can cause up to 30 millisecond overhead per request and * should be used only when appropriate. For performance critical * operations this check should be disabled. * <p> * Default: {@code false} since 4.4 * </p> * * @deprecated (4.4) Use {@link * org.apache.http.impl.conn.PoolingHttpClientConnectionManager#getValidateAfterInactivity()} */ @Deprecated public boolean isStaleConnectionCheckEnabled() { return staleConnectionCheckEnabled; } public static class Builder { private boolean expectContinueEnabled; private HttpHost proxy; private InetAddress localAddress; private boolean staleConnectionCheckEnabled; //...... Builder() { super(); this.staleConnectionCheckEnabled = false; this.redirectsEnabled = true; this.maxRedirects = 50; this.relativeRedirectsAllowed = true; this.authenticationEnabled = true; this.connectionRequestTimeout = -1; this.connectTimeout = -1; this.socketTimeout = -1; this.contentCompressionEnabled = true; this.normalizeUri = true; } /** * @deprecated (4.4) Use {@link * org.apache.http.impl.conn.PoolingHttpClientConnectionManager#setValidateAfterInactivity(int)} */ @Deprecated public Builder setStaleConnectionCheckEnabled(final boolean staleConnectionCheckEnabled) { this.staleConnectionCheckEnabled = staleConnectionCheckEnabled; return this; } //...... } }
RequestConfig定義了staleConnectionCheckEnabled屬性,在4.4版本廢棄了,默認為false,替換設置是org.apache.http.impl.conn.PoolingHttpClientConnectionManager.setValidateAfterInactivity(int);Builder方法也提供了setStaleConnectionCheckEnabled方法
MainClientExec
org/apache/http/impl/execchain/MainClientExec.java
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"); } 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; } } context.setAttribute(HttpCoreContext.HTTP_REQUEST, request); response = requestExecutor.execute(request, managedConn, context); //...... } } //...... //...... }
MainClientExec的execute先通過connManager.requestConnection獲取ConnectionRequest,然后通過connRequest.get(timeout > 0 ? timeout : 0, TimeUnit.MILLISECONDS)獲取managedConn,之后判斷requestConfig的isStaleConnectionCheckEnabled,為true的話,會執(zhí)行連接的校驗,先判斷是否open,再判斷是否stale,為stale的話則執(zhí)行close;在managedConn關閉的時候,會通過establishRoute再進行連接
establishRoute
org/apache/http/impl/execchain/MainClientExec.java
/** * Establishes the target route. */ void establishRoute( final AuthState proxyAuthState, final HttpClientConnection managedConn, final HttpRoute route, final HttpRequest request, final HttpClientContext context) throws HttpException, IOException { final RequestConfig config = context.getRequestConfig(); final int timeout = config.getConnectTimeout(); final RouteTracker tracker = new RouteTracker(route); int step; do { final HttpRoute fact = tracker.toRoute(); step = this.routeDirector.nextStep(route, fact); switch (step) { case HttpRouteDirector.CONNECT_TARGET: this.connManager.connect( managedConn, route, timeout > 0 ? timeout : 0, context); tracker.connectTarget(route.isSecure()); break; case HttpRouteDirector.CONNECT_PROXY: this.connManager.connect( managedConn, route, timeout > 0 ? timeout : 0, context); final HttpHost proxy = route.getProxyHost(); tracker.connectProxy(proxy, route.isSecure() && !route.isTunnelled()); break; case HttpRouteDirector.TUNNEL_TARGET: { final boolean secure = createTunnelToTarget( proxyAuthState, managedConn, route, request, context); this.log.debug("Tunnel to target created."); tracker.tunnelTarget(secure); } break; case HttpRouteDirector.TUNNEL_PROXY: { // The most simple example for this case is a proxy chain // of two proxies, where P1 must be tunnelled to P2. // route: Source -> P1 -> P2 -> Target (3 hops) // fact: Source -> P1 -> Target (2 hops) final int hop = fact.getHopCount()-1; // the hop to establish final boolean secure = createTunnelToProxy(route, hop, context); this.log.debug("Tunnel to proxy created."); tracker.tunnelProxy(route.getHopTarget(hop), secure); } break; case HttpRouteDirector.LAYER_PROTOCOL: this.connManager.upgrade(managedConn, route, context); tracker.layerProtocol(route.isSecure()); break; case HttpRouteDirector.UNREACHABLE: throw new HttpException("Unable to establish route: " + "planned = " + route + "; current = " + fact); case HttpRouteDirector.COMPLETE: this.connManager.routeComplete(managedConn, route, context); break; default: throw new IllegalStateException("Unknown step indicator " + step + " from RouteDirector."); } } while (step > HttpRouteDirector.COMPLETE); }
establishRoute方法在循環(huán)里頭通過connManager.connect建立連接
requestConnection
org/apache/http/impl/conn/PoolingHttpClientConnectionManager.java
public ConnectionRequest requestConnection( final HttpRoute route, final Object state) { Args.notNull(route, "HTTP route"); if (this.log.isDebugEnabled()) { this.log.debug("Connection request: " + format(route, state) + formatStats(route)); } final Future<CPoolEntry> future = this.pool.lease(route, state, null); return new ConnectionRequest() { @Override public boolean cancel() { return future.cancel(true); } @Override public HttpClientConnection get( final long timeout, final TimeUnit timeUnit) throws InterruptedException, ExecutionException, ConnectionPoolTimeoutException { final HttpClientConnection conn = leaseConnection(future, timeout, timeUnit); if (conn.isOpen()) { final HttpHost host; if (route.getProxyHost() != null) { host = route.getProxyHost(); } else { host = route.getTargetHost(); } final SocketConfig socketConfig = resolveSocketConfig(host); conn.setSocketTimeout(socketConfig.getSoTimeout()); } return conn; } }; }
PoolingHttpClientConnectionManager的requestConnection返回的ConnectionRequest的get方法是通過leaseConnection(future, timeout, timeUnit)來獲取連接的,而leaseConnection依賴的是pool.lease(route, state, null)返回的future
lease
org/apache/http/pool/AbstractConnPool.java
/** * {@inheritDoc} * <p> * Please note that this class does not maintain its own pool of execution * {@link Thread}s. Therefore, one <b>must</b> call {@link Future#get()} * or {@link Future#get(long, TimeUnit)} method on the {@link Future} * returned by this method in order for the lease operation to complete. */ @Override public Future<E> lease(final T route, final Object state, final FutureCallback<E> callback) { Args.notNull(route, "Route"); Asserts.check(!this.isShutDown, "Connection pool shut down"); return new Future<E>() { private final AtomicBoolean cancelled = new AtomicBoolean(false); private final AtomicBoolean done = new AtomicBoolean(false); private final AtomicReference<E> entryRef = new AtomicReference<E>(null); @Override public boolean cancel(final boolean mayInterruptIfRunning) { if (done.compareAndSet(false, true)) { cancelled.set(true); lock.lock(); try { condition.signalAll(); } finally { lock.unlock(); } if (callback != null) { callback.cancelled(); } return true; } return false; } @Override public boolean isCancelled() { return cancelled.get(); } @Override public boolean isDone() { return done.get(); } @Override public E get() throws InterruptedException, ExecutionException { try { return get(0L, TimeUnit.MILLISECONDS); } catch (final TimeoutException ex) { throw new ExecutionException(ex); } } @Override public E get(final long timeout, final TimeUnit timeUnit) throws InterruptedException, ExecutionException, TimeoutException { for (;;) { synchronized (this) { try { final E entry = entryRef.get(); if (entry != null) { return entry; } if (done.get()) { throw new ExecutionException(operationAborted()); } final E leasedEntry = getPoolEntryBlocking(route, state, timeout, timeUnit, this); if (validateAfterInactivity > 0) { if (leasedEntry.getUpdated() + validateAfterInactivity <= System.currentTimeMillis()) { if (!validate(leasedEntry)) { leasedEntry.close(); release(leasedEntry, false); continue; } } } if (done.compareAndSet(false, true)) { entryRef.set(leasedEntry); done.set(true); onLease(leasedEntry); if (callback != null) { callback.completed(leasedEntry); } return leasedEntry; } else { release(leasedEntry, true); throw new ExecutionException(operationAborted()); } } catch (final IOException ex) { if (done.compareAndSet(false, true)) { if (callback != null) { callback.failed(ex); } } throw new ExecutionException(ex); } } } } }; }
lease返回的future的get(final long timeout, final TimeUnit timeUnit)方法在一個循環(huán)里頭去獲取連接,內部是通過getPoolEntryBlocking獲取到leasedEntry,然后在validateAfterInactivity大于0的時候進行連接校驗,在leasedEntry.getUpdated() + validateAfterInactivity <= System.currentTimeMillis()的時候進行validate,校驗不成功的話進行關閉掉leasedEntry,然后release掉leasedEntry,然后繼續(xù)循環(huán)獲取下一個連接
小結
httpclient的RequestConfig提供了staleConnectionCheckEnabled屬性用于在請求獲取到連接的時候進行連接檢測,不過這個屬性在4.4版本被廢棄了,并默認設置為false,替換設置是org.apache.http.impl.conn.PoolingHttpClientConnectionManager.setValidateAfterInactivity(int),它是在ConnectionRequest的lease方法里頭根據(jù)leasedEntry.getUpdated() + validateAfterInactivity判斷是否需要校驗連接,若需要且校驗不通過則循環(huán)繼續(xù)獲??;而staleConnectionCheckEnabled則是在requestConnection之后根據(jù)RequestConfig的isStaleConnectionCheckEnabled來判斷,然后進行連接校驗,校驗不通過則關閉managedConn,最后會在判斷如果managedConn.isOpen()為false,則執(zhí)行establishRoute,在循環(huán)里頭通過connManager.connect來建立新連接。
以上就是httpclient staleConnectionCheckEnabled獲取連接流程解析的詳細內容,更多關于httpclient staleConnectionCheckEnabled的資料請關注腳本之家其它相關文章!
相關文章
JAVA實戰(zhàn)練習之圖書管理系統(tǒng)實現(xiàn)流程
隨著網(wǎng)絡技術的高速發(fā)展,計算機應用的普及,利用計算機對圖書館的日常工作進行管理勢在必行,本篇文章手把手帶你用Java實現(xiàn)一個圖書管理系統(tǒng),大家可以在過程中查缺補漏,提升水平2021-10-10Mybatis一對多關聯(lián)關系映射實現(xiàn)過程解析
這篇文章主要介紹了Mybatis一對多關聯(lián)關系映射實現(xiàn)過程解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-02-02SpringBoot集成JWT的工具類與攔截器實現(xiàn)方式
這篇文章主要介紹了SpringBoot集成JWT的工具類與攔截器實現(xiàn)方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-01-01SpringBoot實現(xiàn)過濾器Filter的三種方式
過濾器Filter由Servlet提供,基于函數(shù)回調實現(xiàn)鏈式對網(wǎng)絡請求與響應的攔截與修改,本文講給大家詳細介紹SpringBoot實現(xiàn)過濾器Filter的三種方式,需要的朋友可以參考下2023-08-08springboot使用redisTemplate操作lua腳本
本文主要介紹了springboot使用redisTemplate操作lua腳本,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2022-08-08