HttpClient 4.5教程学习与翻译——第7章:高级主题

原文链接

第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会话的用户主体。如果两者都不可用,则返回空令牌。

CloseableHttpClient httpclient = HttpClients.createDefault();
HttpClientContext context = HttpClientContext.create();
HttpGet httpget = new HttpGet("http://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("http://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("http://localhost:8080/");
CloseableHttpResponse response2 = httpclient.execute(httpget2, context2);
try {
    HttpEntity entity2 = response2.getEntity();
} finally {
    response2.close();
}

7.3. 使用FutureRequestExecutionService

使用FutureRequestExecutionService,您可以安排http调用并将响应视为Future。例如,在多次调用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("http://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. 回调

您也可以使用FutureCallback实例在请求完成时获取回调,而不是手动调用task.get()。这与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("http://www.google.com"), HttpClientContext.create(),
    new OkidokiHandler(), new MyCallback());

7.3.5. 度量

FutureRequestExecutionService通常用于进行大量Web服务调用的应用程序中。为了便于例如通过监视或配置调优,FutureRequestExecutionService可以跟踪多个指标。
每个HttpRequestFutureTask都提供了获取任务计划,启动和结束时间的方法。此外,还可以使用请求和任务持续时间。这些度量标准在FutureRequestExecutionMetrics实例中的FutureRequestExecutionService中聚合,可以通过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

上一章: 缓存

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335