Part2-HttpClient官方教程-Chapter7-高级主题(Advanced topics) (HTTP Caching)
7.1 自定义客户端连接
在某些情况下,为了能够处理非标准的、不兼容的行为,可能需要自定义HTTP消息通过网络传输的方式,而不是使用HTTP参数。例如,对于web爬虫,可能有必要迫使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接口来确定给定的执行上下文是否是用户特定的。如果上下文是特定于用户的,或者如果上下文不包含特定于当前用户的任何资源或细节,则该处理程序返回的令牌对象将惟一地标识当前用户。用户令牌将用于确保用户特定的资源不会被其他用户共享或重用。
UserTokenHandler接口的默认实现使用一个主类的实例来表示HTTP连接的状态对象,如果它可以从给定的执行上下文获得。DefaultUserTokenHandler将使用基于连接的身份验证方案的用户主体,如NTLM或使用客户身份验证的SSL会话。如果两者都不可用,将返回null令牌。
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调用并将响应视为未来。 这是很有用的,例如, 多次调用Web服务。 使用FutureRequestExecutionService的优点是可以使用多个线程同时调度请求,设置任务超时或在不再需要响应时取消它们。
FutureRequestExecutionService封装请求HttpRequestFutureTask,FutureTask延伸。这个类允许您取消任务,并跟踪各种度量,例如请求持续时间。
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。因为请求是由executor服务处理的,所以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()方法;否则,响应将被简单地忽略,但是请求将被允许正常完成。任何后续的任务调用都将抛出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("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
Part2-HttpClient官方教程-Chapter7-高级主题(Advanced topics) (HTTP Caching)的更多相关文章
- RabbitMQ入门教程(七):主题交换机Topics
原文:RabbitMQ入门教程(七):主题交换机Topics 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog. ...
- ActionBar官方教程(2)选主题让应用支或不支持ActionBar及支持ActionBar的应用如何隐藏和显示
Adding the Action Bar As mentioned above, this guide focuses on how to use the ActionBar APIs in the ...
- Python-第三方库requests详解(附requests中文官方教程)
转自http://blog.csdn.net/cyjs1988/article/details/73294774 Python+requests中文官方教程: http://www.python-re ...
- Unity性能优化(3)-官方教程Optimizing garbage collection in Unity games翻译
本文是Unity官方教程,性能优化系列的第三篇<Optimizing garbage collection in Unity games>的翻译. 相关文章: Unity性能优化(1)-官 ...
- Unity性能优化(4)-官方教程Optimizing graphics rendering in Unity games翻译
本文是Unity官方教程,性能优化系列的第四篇<Optimizing graphics rendering in Unity games>的翻译. 相关文章: Unity性能优化(1)-官 ...
- MongoDB_C_Driver使用教程(2)高级连接
高级连接(Advanced Connection) 以下指南包含MongoDB配置的特定类型的信息. 简单的连接到独立服务器的示例,请参考MongoDB_C_Dirver使用教程. 要连接到启用身份验 ...
- 详解 “Android UI”设计官方教程
我们曾经给大家一个<MeeGo移动终端设备开发UI设计基础教程>,同时很多朋友都在寻找Android UI开发的教程,我们从Android的官方开发者博客找了一份幻灯片,介绍了一些Andr ...
- Python:高级主题之(属性取值和赋值过程、属性描述符、装饰器)
Python:高级主题之(属性取值和赋值过程.属性描述符.装饰器) 背景 学习了Javascript才知道原来属性的取值和赋值操作访问的“位置”可能不同.还有词法作用域这个东西,这也是我学习任何一门语 ...
- [译]RabbitMQ教程C#版 - 主题
先决条件 本教程假定 RabbitMQ 已经安装,并运行在localhost标准端口(5672).如果你使用不同的主机.端口或证书,则需要调整连接设置. 从哪里获得帮助 如果您在阅读本教程时遇到困难, ...
随机推荐
- Chromium之工程类别
虽然有700多个project,其实有很多是不成声二进制执行文件的,他们主要是调用cygwin的环境,执行一些python的脚本. 具体这个.py文件做了哪些共工作,还要再研究,目前看到有打包一些.p ...
- 【bzoj4832】[Lydsy2017年4月月赛]抵制克苏恩 概率期望dp
题目描述 你分别有a.b.c个血量为1.2.3的奴隶主,假设英雄血量无限,问:如果对面下出一个K点攻击力的克苏恩,你的英雄期望会受到到多少伤害. 输入 输入包含多局游戏. 第一行包含一个整数 T (T ...
- 新浪云部署java web程序 注意事项
在新浪云新手指南里有部署java的示例,但是对一个新手来说难免会有一些地方犯错,折腾了好长时间才把自己的java web部署到了新浪云.这里主要写一些我遇到的问题与第一次使用新浪云的朋友分享一下. 首 ...
- IO模式
二 IO模式 刚才说了,对于一次IO访问(以read举例),数据会先被拷贝到操作系统内核的缓冲区中,然后才会从操作系统内核的缓冲区拷贝到应用程序的地址空间.所以说,当一个read操作发生时,它会经历两 ...
- [bzoj] 1040 骑士 || 基环外向树dp
原题 给出n个点n条边和每个点的点权,一条边的两个断点不能同时选择,问最大可以选多少. //图是一张基环外向树森林 是不是很像舞会啊- 就是多了一条边. 所以我们考虑一下对于一棵基环外向树,拆掉一条在 ...
- C++中友元简介
本文基于<C++ Primer(第五版)>,纯属个人笔记整理.若有错误欢迎大家留言指出. 一.为什么要用友元和其缺点? 采用类的机制后实现了数据的隐藏与封装,类的数据成员一般定义为私有成员 ...
- bzoj1901: Zju2112 Dynamic Rankings(BIT套主席树)
带修改的题主席树不记录前缀,只记录单点,用BIT统计前缀. 对于BIT上每一个点建一棵主席树,修改和询问的时候用BIT跑,在主席树上做就行了. 3k4人AC的题#256...应该不算慢 #incl ...
- SELECT LAST_INSERT_ID() 的使用和注意事项
SELECT LAST_INSERT_ID() 的使用和注意事项 尊重个人劳动成果,转载请注明出处: http://blog.csdn.net/czd3355/article/details/7130 ...
- 第九章 C99可变长数组VLA详解
C90及C++的数组对象定义是静态联编的,在编译期就必须给定对象的完整信息.但在程序设计过程中,我们常常遇到需要根据上下文环境来定义数组的情况,在运行期才能确知数组的长度.对于这种情况,C90及C++ ...
- Xcode 问题
问题: 昨天在写代码的时候,不知道修改了哪个地方,Xcode6突然犯病了,在当前项目下无法代码提示,但是在新建工程中没有任何问题,其中重装了Xcode6也没有把问题解决, 最终的解决办法是: 在fin ...