HttpClient底层逻辑探究

 HttpClent底层

一句话:HttpClent底层封装了Socket对象
HttpClient httpClient = new DefaultHttpClient();
httpClient.execute(request);
  • CloseableHttpClient
/*** Base implementation of {@link HttpClient} that also implements {@link Closeable}.** @since 4.3*/
@Contract(threading = ThreadingBehavior.SAFE)
public abstract class CloseableHttpClient implements HttpClient, Closeable {/*** {@inheritDoc}*/@Overridepublic CloseableHttpResponse execute(final HttpUriRequest request) throws IOException, ClientProtocolException {return execute(request, (HttpContext) null);}
  • AbstractHttpClient
/ ** @deprecated (4.3) use {@link HttpClientBuilder}.*/
@Contract(threading = ThreadingBehavior.SAFE_CONDITIONAL)
@Deprecated
public abstract class AbstractHttpClient extends CloseableHttpClient {@Overrideprotected final CloseableHttpResponse doExecute(final HttpHost target, final HttpRequest request,final HttpContext context)throws IOException, ClientProtocolException {Args.notNull(request, "HTTP request");// a null target may be acceptable, this depends on the route planner// a null context is acceptable, default context created belowHttpContext execContext = null;RequestDirector director = null;HttpRoutePlanner routePlanner = null;ConnectionBackoffStrategy connectionBackoffStrategy = null;BackoffManager backoffManager = null;// Initialize the request execution context making copies of// all shared objects that are potentially threading unsafe.synchronized (this) {final HttpContext defaultContext = createHttpContext();if (context == null) {execContext = defaultContext;} else {execContext = new DefaultedHttpContext(context, defaultContext);}final HttpParams params = determineParams(request);final RequestConfig config = HttpClientParamConfig.getRequestConfig(params);execContext.setAttribute(ClientContext.REQUEST_CONFIG, config);// Create a director for this requestdirector = createClientRequestDirector(getRequestExecutor(),getConnectionManager(),getConnectionReuseStrategy(),getConnectionKeepAliveStrategy(),getRoutePlanner(),getProtocolProcessor(),getHttpRequestRetryHandler(),getRedirectStrategy(),getTargetAuthenticationStrategy(),getProxyAuthenticationStrategy(),getUserTokenHandler(),params);routePlanner = getRoutePlanner();connectionBackoffStrategy = getConnectionBackoffStrategy();backoffManager = getBackoffManager();}try {if (connectionBackoffStrategy != null && backoffManager != null) {final HttpHost targetForRoute = (target != null) ? target: (HttpHost) determineParams(request).getParameter(ClientPNames.DEFAULT_HOST);final HttpRoute route = routePlanner.determineRoute(targetForRoute, request, execContext);final CloseableHttpResponse out;try {out = CloseableHttpResponseProxy.newProxy(director.execute(target, request, execContext));} catch (final RuntimeException re) {if (connectionBackoffStrategy.shouldBackoff(re)) {backoffManager.backOff(route);}throw re;} catch (final Exception e) {if (connectionBackoffStrategy.shouldBackoff(e)) {backoffManager.backOff(route);}if (e instanceof HttpException) {throw (HttpException)e;}if (e instanceof IOException) {throw (IOException)e;}throw new UndeclaredThrowableException(e);}if (connectionBackoffStrategy.shouldBackoff(out)) {backoffManager.backOff(route);} else {backoffManager.probe(route);}return out;} else {return CloseableHttpResponseProxy.newProxy(director.execute(target, request, execContext));}} catch(final HttpException httpException) {throw new ClientProtocolException(httpException);}}
  • DefaultRequestDirector
 /* @since 4.0** @deprecated Do not use.*/
@Deprecated
public class DefaultRequestDirector implements RequestDirector {/*** Execute request and retry in case of a recoverable I/O failure*/private HttpResponse tryExecute(final RoutedRequest req, final HttpContext context) throws HttpException, IOException {final RequestWrapper wrapper = req.getRequest();final HttpRoute route = req.getRoute();HttpResponse response = null;Exception retryReason = null;for (;;) {// Increment total exec count (with redirects)execCount++;// Increment exec count for this particular requestwrapper.incrementExecCount();if (!wrapper.isRepeatable()) {this.log.debug("Cannot retry non-repeatable request");if (retryReason != null) {throw new NonRepeatableRequestException("Cannot retry request " +"with a non-repeatable request entity.  The cause lists the " +"reason the original request failed.", retryReason);} else {throw new NonRepeatableRequestException("Cannot retry request " +"with a non-repeatable request entity.");}}try {if (!managedConn.isOpen()) {// If we have a direct route to the target host// just re-open connection and re-try the requestif (!route.isTunnelled()) {this.log.debug("Reopening the direct connection.");managedConn.open(route, context, params);} else {// otherwise give upthis.log.debug("Proxied connection. Need to start over.");break;}}if (this.log.isDebugEnabled()) {this.log.debug("Attempt " + execCount + " to execute request");}response = requestExec.execute(wrapper, managedConn, context);break;} catch (final IOException ex) {this.log.debug("Closing the connection.");try {managedConn.close();} catch (final IOException ignore) {}if (retryHandler.retryRequest(ex, wrapper.getExecCount(), context)) {if (this.log.isInfoEnabled()) {this.log.info("I/O exception ("+ ex.getClass().getName() +") caught when processing request to "+ route +": "+ ex.getMessage());}if (this.log.isDebugEnabled()) {this.log.debug(ex.getMessage(), ex);}if (this.log.isInfoEnabled()) {this.log.info("Retrying request to " + route);}retryReason = ex;} else {if (ex instanceof NoHttpResponseException) {final NoHttpResponseException updatedex = new NoHttpResponseException(route.getTargetHost().toHostString() + " failed to respond");updatedex.setStackTrace(ex.getStackTrace());throw updatedex;} else {throw ex;}}}}return response;}
  • HttpRequestExecutor
    
   / ** @since 4.0*/
@Contract(threading = ThreadingBehavior.IMMUTABLE)
public class HttpRequestExecutor {/*** Send the given request over the given connection.* <p>* This method also handles the expect-continue handshake if necessary.* If it does not have to handle an expect-continue handshake, it will* not use the connection for reading or anything else that depends on* data coming in over the connection.** @param request   the request to send, already*                  {@link #preProcess preprocessed}* @param conn      the connection over which to send the request,*                  already established* @param context   the context for sending the request** @return  a terminal response received as part of an expect-continue*          handshake, or*          {@code null} if the expect-continue handshake is not used** @throws IOException in case of an I/O error.* @throws HttpException in case of HTTP protocol violation or a processing*   problem.*/protected HttpResponse doSendRequest(final HttpRequest request,final HttpClientConnection conn,final HttpContext context) throws IOException, HttpException {Args.notNull(request, "HTTP request");Args.notNull(conn, "Client connection");Args.notNull(context, "HTTP context");HttpResponse response = null;context.setAttribute(HttpCoreContext.HTTP_CONNECTION, conn);context.setAttribute(HttpCoreContext.HTTP_REQ_SENT, Boolean.FALSE);conn.sendRequestHeader(request);if (request instanceof HttpEntityEnclosingRequest) {// Check for expect-continue handshake. We have to flush the// headers and wait for an 100-continue response to handle it.// If we get a different response, we must not send the entity.boolean sendentity = true;final ProtocolVersion ver =request.getRequestLine().getProtocolVersion();if (((HttpEntityEnclosingRequest) request).expectContinue() &&!ver.lessEquals(HttpVersion.HTTP_1_0)) {conn.flush();// As suggested by RFC 2616 section 8.2.3, we don't wait for a// 100-continue response forever. On timeout, send the entity.if (conn.isResponseAvailable(this.waitForContinue)) {response = conn.receiveResponseHeader();if (canResponseHaveBody(request, response)) {conn.receiveResponseEntity(response);}final int status = response.getStatusLine().getStatusCode();if (status < 200) {if (status != HttpStatus.SC_CONTINUE) {throw new ProtocolException("Unexpected response: " + response.getStatusLine());}// discard 100-continueresponse = null;} else {sendentity = false;}}}if (sendentity) {conn.sendRequestEntity((HttpEntityEnclosingRequest) request);}}conn.flush();context.setAttribute(HttpCoreContext.HTTP_REQ_SENT, Boolean.TRUE);return response;}
  • DefaultBHttpClientConnection
/*** Default implementation of {@link HttpClientConnection}.** @since 4.3*/
public class DefaultBHttpClientConnection extends BHttpConnectionBaseimplements HttpClientConnection {   @Overridepublic void receiveResponseEntity(final HttpResponse response) throws HttpException, IOException {Args.notNull(response, "HTTP response");ensureOpen();final HttpEntity entity = prepareInput(response);response.setEntity(entity);}
  • BHttpConnectionBase

/*** This class serves as a base for all {@link org.apache.http.HttpConnection} implementations* and provides functionality common to both client and server HTTP connections.** @since 4.0*/
public class BHttpConnectionBase implements HttpInetConnection {protected void ensureOpen() throws IOException {final Socket socket = this.socketHolder.get();if (socket == null) {throw new ConnectionClosedException();}if (!this.inBuffer.isBound()) {this.inBuffer.bind(getSocketInputStream(socket));}if (!this.outbuffer.isBound()) {this.outbuffer.bind(getSocketOutputStream(socket));}}

HttpClient使用和拓展

拓展

  • URLConnection

HttpClient相比传统JDK自带的URLConnection,增加了易用性和灵活性,它不仅使客户端发送Http请求变得容易,而且也方便开发人员测试接口(基于Http协议的),提高了开发的效率,也方便提高代码的健壮性。因此熟练掌握HttpClient是很重要的必修内容,掌握HttpClient后,相信对于Http协议的了解会更加深入。

  • Commons HttpClient和HttpClient区别

org.apache.commons.httpclient.HttpClient与org.apache.http.client.HttpClient的区别

Commons的HttpClient项目现在是生命的尽头,不再被开发,  已被Apache HttpComponents项目HttpClient和HttpCore  模组取代,提供更好的性能和更大的灵活性。  

使用

省略,参考原文链接:https://blog.csdn.net/w372426096/article/details/82713315