閱讀692 返回首頁    go 阿裏雲 go 技術社區[雲棲]


《Http Client 官方文檔》7. 高級主題

第七章 高級主題

7.1. 自定義客戶端連接

在某些情況下,有必要自定義HTTP消息傳輸的方式來擴展HTTP參數的可使用性,以便能夠處理非標準的作業。 例如,對於網絡爬蟲,可能需要強製HttpClient接受格式不正確的響應頭,來捕捉消息的內容。

通常,插入自定義消息解析器或自定義連接實現的過程涉及幾個步驟:

  • 提供自定義的“LineParser”/“LineFormatter”接口實現類。 根據需要實現消息解析/格式化邏輯。

    class MyLineParser extends BasicLineParser {
    
        @Override
        public Header parseHeader(
                CharArrayBuffer buffer) throws ParseException {
            try {
                return super.parseHeader(buffer);
            } catch (ParseException ex) {
                // Suppress ParseException exception
                return new BasicHeader(buffer.toString(), null);
            }
        }
    
    }
    
  • 提供自定義的“HttpConnectionFactory”接口的實現類。 用自定義的請求寫入器和/或響應解析器根據需要來替換默認的。

    HttpConnectionFactory<HttpRoute, ManagedHttpClientConnection> connFactory =
            new ManagedHttpClientConnectionFactory(
                new DefaultHttpRequestWriterFactory(),
                new DefaultHttpResponseParserFactory(
                        new MyLineParser(), new DefaultHttpResponseFactory()));
    
  • 配置HttpClient來使用自定義連接工廠類。

    PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(
        connFactory);
    CloseableHttpClient httpclient = HttpClients.custom()
            .setConnectionManager(cm)
            .build();
    

7.2. 有狀態的HTTP連接

雖然HTTP規範假設會話狀態信息總是以HTTP cookie的形式嵌入HTTP消息中,因此HTTP連接始終是無狀態的,但這種假設在現實生活中並不總是成立。有些情況下,使用特定身份的用戶或在特定的安全上下文內創建的HTTP連接是不能與其他用戶共享的,隻能由相同用戶重複使用。有狀態HTTP連接的例子是“NTLM”認證的連接和具有客戶端證書認證的SSL連接。

7.2.1. 用戶令牌處理器

HttpClient依賴於’UserTokenHandler’接口來確定給定的執行上下文是否為用戶特定的。如果是,則該處理程序返回的令牌對象應該唯一標識當前用戶,如果上下文不包含當前用戶特有的任何資源或詳細信息,則該對象將為null。 用戶令牌將用於確保用戶特定資源不會被其他用戶共享或重複使用。

‘UserTokenHandler’接口的默認實現類作為Principal類的實例來表示HTTP連接的狀態對象,如果它可以從給定的執行上下文獲得的話。
‘DefaultUserTokenHandler’將使用基於連接的認證方案(例如’NTLM’)或使用客戶端認證的SSL會話的用戶主體。 如果兩者都不可用,將返回NULL令牌。

CloseableHttpClient httpclient = HttpClients.createDefault();
HttpClientContext context = HttpClientContext.create();
HttpGet httpget = new HttpGet("https://localhost:8080/");
CloseableHttpResponse response = httpclient.execute(httpget, context);
try {
    Principal principal = context.getUserToken(Principal.class);
    System.out.println(principal);
} finally {
    response.close();
}

如果默認設置不能滿足需要,用戶可以提供自定義實現:

UserTokenHandler userTokenHandler = new UserTokenHandler() {

    public Object getUserToken(HttpContext context) {
        return context.getAttribute("my-token");
    }

};
CloseableHttpClient httpclient = HttpClients.custom()
        .setUserTokenHandler(userTokenHandler)
        .build();

7.2.2. 持久狀態連接

請注意,隻有當執行請求時相同的狀態對象綁定到執行上下文時,攜帶狀態對象的持久連接才可重複使用。因此,確保用戶執行後續HTTP請求時重用了相同的上下文或用戶令牌在請求執行之前就被綁定到上下文,是非常重要的。

CloseableHttpClient httpclient = HttpClients.createDefault();
HttpClientContext context1 = HttpClientContext.create();
HttpGet httpget1 = new HttpGet("https://localhost:8080/");
CloseableHttpResponse response1 = httpclient.execute(httpget1, context1);
try {
    HttpEntity entity1 = response1.getEntity();
} finally {
    response1.close();
}
Principal principal = context1.getUserToken(Principal.class);

HttpClientContext context2 = HttpClientContext.create();
context2.setUserToken(principal);
HttpGet httpget2 = new HttpGet("https://localhost:8080/");
CloseableHttpResponse response2 = httpclient.execute(httpget2, context2);
try {
    HttpEntity entity2 = response2.getEntity();
} finally {
    response2.close();
}

7.3. 使用FutureRequestExecutionService

使用FutureRequestExecutionService,您可以有計劃的調用http並且在未來做出響應。 這是很有用的,例如, 對Web服務進行多個調用。使用FutureRequestExecutionService的優點是,您可以使用多個線程來並發調度請求,也可以設置這些任務超時,或在不再需要響應的時候取消它們。

FutureRequestExecutionService使用擴展了FutureTask的HttpRequestFutureTask類來封裝請求。該類允許您取消任務以及追蹤各個數據,如請求持續時間。

7.3.1. 創建FutureRequestExecutionService

futureRequestExecutionService類的構造函數接受任何已有的httpClient實例和ExecutorService實例。 當配置這兩者時,重要的是將最大連接數與要使用的線程數一致。當線程數超過了最大連接數時,因為沒有了可用的連接,連接將會進入超時。而出現相反的情況時,futureRequestExecutionService將不會使用它們

HttpClient httpClient = HttpClientBuilder.create().setMaxConnPerRoute(5).build();
ExecutorService executorService = Executors.newFixedThreadPool(5);
FutureRequestExecutionService futureRequestExecutionService =
    new FutureRequestExecutionService(httpClient, executorService);

7.3.2. 計劃請求

要計劃請求,隻需提供一個HttpUriRequest,HttpContext和一個ResponseHandler。 因為請求由執行器服務處理,所以會強製使用ResponseHandler。

private final class OkidokiHandler implements ResponseHandler<Boolean> {
    public Boolean handleResponse(
            final HttpResponse response) throws ClientProtocolException, IOException {
        return response.getStatusLine().getStatusCode() == 200;
    }
}

HttpRequestFutureTask<Boolean> task = futureRequestExecutionService.execute(
    new HttpGet("https://www.google.com"), HttpClientContext.create(),
    new OkidokiHandler());
// blocks until the request complete and then returns true if you can connect to Google
boolean ok=task.get();

7.3.3. 取消任務

計劃任務有可能被取消。如果任務一直就在隊列中等待執行,那麼它將永遠不會執行。如果它正在執行中並且mayInterruptIfRunning參數為true,那麼將在請求上調用abort(); 否則雖然會正常完成請求,但是會忽略響應。任何對task.get()的後續調用都將失敗並返回IllegalStateException異常。 應當注意,取消任務僅會釋放客戶端資源。 該請求實際上可以在服務器端正常處理。

task.cancel(true)
task.get() // throws an Exception

7.3.4. 回調

如果要代替手動調用task.get(),還可以使用FutureCallback實例在請求完成時獲取回調, 它與HttpAsyncClient中使用的接口相同。

private final class MyCallback implements FutureCallback<Boolean> {

    public void failed(final Exception ex) {
        // do something
    }

    public void completed(final Boolean result) {
        // do something
    }

    public void cancelled() {
        // do something
    }
}

HttpRequestFutureTask<Boolean> task = futureRequestExecutionService.execute(
    new HttpGet("https://www.google.com"), HttpClientContext.create(),
    new OkidokiHandler(), new MyCallback());

7.3.5. 數據

FutureRequestExecutionService通常用於進行大量Web服務調用的應用程序中。 為了方便監控或配置調整等,FutureRequestExecutionService會追蹤多項數據。

每個HttpRequestFutureTask提供了獲取任務調度,啟動和結束時間的方法。 此外,請求和任務持續時間也可用。這些數據標準在FutureRequestExecutionService中的FutureRequestExecutionMetrics實例中聚合,可通過FutureRequestExecutionService.metrics()訪問。

task.scheduledTime() // returns the timestamp the task was scheduled
task.startedTime() // returns the timestamp when the task was started
task.endedTime() // returns the timestamp when the task was done executing
task.requestDuration // returns the duration of the http request
task.taskDuration // returns the duration of the task from the moment it was scheduled

FutureRequestExecutionMetrics metrics = futureRequestExecutionService.metrics()
metrics.getActiveConnectionCount() // currently active connections
metrics.getScheduledConnectionCount(); // currently scheduled connections
metrics.getSuccessfulConnectionCount(); // total number of successful requests
metrics.getSuccessfulConnectionAverageDuration(); // average request duration
metrics.getFailedConnectionCount(); // total number of failed tasks
metrics.getFailedConnectionAverageDuration(); // average duration of failed tasks
metrics.getTaskCount(); // total number of tasks scheduled
metrics.getRequestCount(); // total number of requests
metrics.getRequestAverageDuration(); // average request duration
metrics.getTaskAverageDuration(); // average task duration

轉載自 並發編程網 - ifeve.com

最後更新:2017-05-19 11:31:47

  上一篇:go  Spring Data 4.4-4.5翻譯
  下一篇:go  《HttpClient 官方文檔》第三章 HTTP 狀態管理