HttpClient原始碼解析系列:第二篇:極簡版實現
阿新 • • 發佈:2019-02-06
極簡版的實現,核心架構的原初模型
從MinimalHttpClient從名字可以看出,是一個極簡可以用的版本,是核心設計的原初模型。所以我們就從最精簡的開始分析。
核心元素只有三個,一個引數 params,一個執行器 requestExecutor,一個連線管理器 connManager。
再來看核心的執行方法
MinimalClientExec:是ClientExecChain的一個實現,只是封裝了最基本的HTTP過程,提供最直接的客戶端伺服器互動,不支援代理,不支援在各種情況下的重試(重定向,許可權校驗,IO異常等)。
所以,我們在往下一層,看 MinimalClientExec 是如何執行的,由於程式碼很長,略過非核心的程式碼用省略代替。
除了上面說說的,外圍的管理和處理,最核心的就在於Http請求是如何發出的,如何拿到最原始的資料返回。這就是
HttpRequestExecutor 做的事情。其核心流程是基於 blocking (classic) I/O 模型的。同樣,我們略去非核心的部分,只留下核心程式碼。
通過上面的程式碼,可以發現,最終發出請求和收取請求內容,是 HttpClientConnection 來完成的。Executor只是封裝了這個過程,並完成了外圍處理。至於 HttpClientConnection 的功能設計和底層Socket實現,之後再討論。
到這裡,其實Http請求的發出收取處理管理等工作的最基礎版本(Minimal)就已經非常清楚了。最後理一下MinimalHttpClient最相關的核心類:
可以看到,一個簡單的 傳送-收取 過程被分成了眾多的類來分別處理。看上去很多,實際上也就三個步驟:管理請求(ConnectionManager),執行請求(ReqeustExecutor),處理請求(HttpProcessor)。
而每個步驟都有不少相關的東西,但是主線邏輯是非常清晰的。這裡基本把最核心的執行鏈講完了,下面是分開討論其他問題。
HttpClientConnectionManager(@since 4.3):
連線池元件,管理連線的整個生命週期。連線在連線池中建立、複用以及移除。Connection manager封裝了對連線池的具體操作,比如向連線池租用和歸還連線。
Connection被創建出來後處於閒置狀態,由連線池管理,呼叫時會校驗是否是open狀態,不是的話會進行connect。connect的過程就是 基於不同schema(主要是http和https)建立不同的socket連線(ssl和plain)並且將http請求(連線)繫結到socket。同時連線也會因為心跳或者過期等原因被close變成stale狀態,直至被下一次get到時或者連線滿時被清理出去。
同時連線池還能對連線進行限流–全域性和單route連線數。
ClientExecChain: |
@Override
protected CloseableHttpResponse doExecute(
final HttpHost target,
final HttpRequest request,
final HttpContext context) throws IOException, ClientProtocolException {
Args.notNull(target, "Target host");
Args.notNull(request, "HTTP request");
HttpExecutionAware execAware = null;
if (request instanceof HttpExecutionAware) {
execAware = (HttpExecutionAware) request;
}
try {
final HttpRequestWrapper wrapper = HttpRequestWrapper.wrap(request);
final HttpClientContext |
HttpExecutionAware:一個類可能發生blocking I/O可以繼承這個介面,這樣當blocking I/O被取消的時候,繼承這個介面的類會收到通知。 HttpRequestWrapper:HttpRequest的包裝類,可以保證在修改Request的時候原始請求物件不會被改變。 HttpRoute:指示對方伺服器地址。HttpRoutePlanner用來建立HttpRoute。後者代表客戶端request的對端伺服器,主要包含rout的host以及proxy資訊。 RequestConfig:一些與HTTP請求相關的基礎設定。 |
最終,落實到 requestExecutor 去執行。 public MinimalHttpClient( final HttpClientConnectionManager connManager) { super(); this.connManager = Args.notNull(connManager, "HTTP connection manager"); this.requestExecutor = newMinimalClientExec( new HttpRequestExecutor(), connManager, DefaultConnectionReuseStrategy.INSTANCE, DefaultConnectionKeepAliveStrategy.INSTANCE); this.params = new BasicHttpParams(); } |
private final HttpRequestExecutor requestExecutor; private final HttpClientConnectionManager connManager; private final ConnectionReuseStrategy reuseStrategy; private final ConnectionKeepAliveStrategy keepAliveStrategy; private final HttpProcessor httpProcessor; public MinimalClientExec( final HttpRequestExecutor requestExecutor, final HttpClientConnectionManager connManager, final ConnectionReuseStrategy reuseStrategy, final ConnectionKeepAliveStrategy keepAliveStrategy) { Args.notNull(requestExecutor, "HTTP request executor"); Args.notNull(connManager, "Client connection manager"); Args.notNull(reuseStrategy, "Connection reuse strategy"); Args.notNull(keepAliveStrategy, "Connection keep alive strategy"); this.httpProcessor = new ImmutableHttpProcessor( new RequestContent(), new RequestTargetHost(), new RequestClientConnControl(), new RequestUserAgent(VersionInfo.getUserAgent( "Apache-HttpClient", "org.apache.http.client", getClass()))); this.requestExecutor = requestExecutor; this.connManager = connManager; this.reuseStrategy = reuseStrategy; this.keepAliveStrategy = keepAliveStrategy; } |
public CloseableHttpResponse execute(){ 。。。。 final ConnectionRequest connRequest = connManager.requestConnection(route, null); 。。。。 final HttpClientConnection managedConn = connRequest.get(timeout > 0 ? timeout : 0, TimeUnit.MILLISECONDS); 。。。。 context.setAttribute(HttpCoreContext.HTTP_TARGET_HOST, target); context.setAttribute(HttpCoreContext.HTTP_REQUEST, request); context.setAttribute(HttpCoreContext.HTTP_CONNECTION, managedConn); context.setAttribute(HttpClientContext.HTTP_ROUTE, route); 。。。。 httpProcessor.process(request, context); final HttpResponse response = requestExecutor.execute(request, managedConn, context); httpProcessor.process(response, context); 。。。。 final HttpEntity entity = response.getEntity(); 。。。。 } |
ConnectionRequest:是由ConnectionManager來管理的Request。 HttpClientConnection:Http連線,用於做請求傳送和響應收取。 HttpContext:儲存KV型的資料。主要用於HTTP請求過程中,多個邏輯過程之間的資料共享。 ImmutableHttpProcessor:是HttpProcessor的一個實現,從這個介面的名字就可以看出來,為的是處理Http協議。其中包含了多個協議攔截器,分開處理HTTP協議中的不同部分,這是責任鏈模式的一個典範實現。單純到ImmutableHttpProcessor,就是一系列HttpRequestInterceptor 和 HttpResponseInterceptor。然後按照次序呼叫這些攔截器,處理Request或者Response這兩個過程。 |
public HttpResponse execute(){ 。。。 HttpResponse response = doSendRequest(request, conn, context); if (response == null) { response = doReceiveResponse(request, conn, context); } 。。。 } |
protected HttpResponse doSendRequest( final HttpRequest request, final HttpClientConnection conn, final HttpContext context) throws IOException, HttpException { 。。。。 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-continue response = null; } else { sendentity = false; } } } if (sendentity) { conn.sendRequestEntity((HttpEntityEnclosingRequest) request); } } conn.flush(); context.setAttribute(HttpCoreContext.HTTP_REQ_SENT, Boolean.TRUE); return response; } |
protected HttpResponse doReceiveResponse( final HttpRequest request, final HttpClientConnection conn, final HttpContext context) throws HttpException, IOException { 。。。。。 while (response == null || statusCode < HttpStatus.SC_OK) { response = conn.receiveResponseHeader(); if (canResponseHaveBody(request, response)) { conn.receiveResponseEntity(response); } statusCode = response.getStatusLine().getStatusCode(); } // while intermediate response return response; } |