HttpClient学习记录-系列1(tutorial)
1. HttpClient使用了facade模式,如何使用的?
2. HTTP protocol interceptors使用了Decorator模式,如何使用的?
URIBuilder
response.headerIterator("Set-Cookie");
HeaderElementIterator it = new BasicHeaderElementIterator(
response.headerIterator("Set-Cookie")); 太长了不能直接使用 EntilyUtils.toString(). 否则会浪费大量的内存,正确的使用模式为:
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpGet httpget = new HttpGet("http://localhost/");
CloseableHttpResponse response = httpclient.execute(httpget);
try {
HttpEntity entity = response.getEntity();
if (entity != null) {
long len = entity.getContentLength();
if (len != -1 && len < 2048) {
System.out.println(EntityUtils.toString(entity));
} else {
// Stream content out
}
}
} finally {
response.close();
} 如果想对一个response多次的read可以将entity封装到BufferedHttpEntity,它会将数据读出来缓存到内存里 对response拦截处理:使用ResponseHandler接口 HttpClient是线程安全对象,建议全局唯一
HttpClientContext localContext = HttpClientContext.create(); httpclient.execute(httpget2, context); HttpClientContext实现多个请求数据共享,已经配置传播。
HttpRequestRetryHandler 实现自定义请求重试,注意: 对于自封闭的entity可以重试,非自封闭(流)不可以重试。 redirect是HttpClient自动做的,返回转发以后的结果
第二章: 连接池管理
HttpClientConnectionManager connMrg = new BasicHttpClientConnectionManager();
ConnectionRequest connRequest = connMrg.requestConnection(route, null);
HttpClientConnection conn = connRequest.get(10, TimeUnit.SECONDS); BasicHttpClientConnectionManager只会维护一个Connection,连续的同一个route的请求会复用Connection。不同的route请求会先把当前的连接关掉,再打开一个新的连接。 只能单线程使用 PoolingHttpClientConnectionManager HttpClient默认的连接管理。适用于多线程环境。 每个route默认创建2个并发连接,总共不超过20个连接
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
cm.setMaxTotal(200);
cm.setDefaultMaxPerRoute(20);
HttpHost localhost = new HttpHost("locahost", 80);
cm.setMaxPerRoute(new HttpRoute(localhost), 50);
http.conn-manager.timeout 设置获取请求的超时时间
墙裂建议每个线程维持其自己的HttpContext 由于HttpClient默认的失效连接检测方法并不能完全避免已经失效的连接无法归还到连接池中,所以最好的方法是自己开一个监控线程,定时的去执行Factory的closeExpiredConnections或者closeIdleConnections方法。
public static class IdleConnectionMonitorThread extends Thread { private final HttpClientConnectionManager connMgr;
private volatile boolean shutdown; public IdleConnectionMonitorThread(HttpClientConnectionManager connMgr) {
super();
this.connMgr = connMgr;
} @Override
public void run() {
try {
while (!shutdown) {
synchronized (this) {
wait(5000);
// Close expired connections
connMgr.closeExpiredConnections();
// Optionally, close connections
// that have been idle longer than 30 sec
connMgr.closeIdleConnections(30, TimeUnit.SECONDS);
}
}
} catch (InterruptedException ex) {
// terminate
}
} public void shutdown() {
shutdown = true;
synchronized (this) {
notifyAll();
}
} } 对于自定义某个请求特定的连接持有时间使用ConnectionKeepAliveStrategy。 Http连接底层是使用java.net.Socket来完成网络传输。HttpClient直接使用ConnectionSocketFactory来获取Socket。默认使用PlainConnectionSocketFactory。
HttpClient使用SSLSocketFactory(LayeredConnectionSocketFactory的实现类)创建安全的Socket连接。 可以把SocketFactory和ConnectionFactory组合起来。示例:
ConnectionSocketFactory plainsf = <...>
LayeredConnectionSocketFactory sslsf = <...>
Registry<ConnectionSocketFactory> r = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", plainsf)
.register("https", sslsf)
.build(); HttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(r);
HttpClients.custom()
.setConnectionManager(cm)
.build();
对于不同的协议使用不同的SocketFactory 主机名验证DefaultHostnameVerifier、
NoopHostnameVerifier() 不知道主机验证是个什么鬼东西。后面再研究
HttpClient自定义代理配置:
HttpRoutePlanner重写该类。不知道自定义代理是什么意思。 第三章: Http状态管理
Cookie接口: 抽象的Cookie
SetCookie接口: 表示Set-Cookie头信息
ClientCookie: 理解为客户端使用Cookie对象的抽象 Cookie有很多标准:
Standard strict
Standard
Netscape draft (obsolete)
RFC 2965 (obsolete)
RFC 2109 (obsolete)
Browser compatibility (obsolete)
Default
Ignore cookies
建议使用Standard和Standard strict。 这些标准对应于CookieSpecs 自定义Cookie标准:
PublicSuffixMatcher publicSuffixMatcher = PublicSuffixMatcherLoader.getDefault(); Registry<CookieSpecProvider> r = RegistryBuilder.<CookieSpecProvider>create()
.register(CookieSpecs.DEFAULT,
new DefaultCookieSpecProvider(publicSuffixMatcher))
.register(CookieSpecs.STANDARD,
new RFC6265CookieSpecProvider(publicSuffixMatcher))
.register("easy", new EasySpecProvider())
.build(); RequestConfig requestConfig = RequestConfig.custom()
.setCookieSpec("easy")
.build(); CloseableHttpClient httpclient = HttpClients.custom()
.setDefaultCookieSpecRegistry(r)
.setDefaultRequestConfig(requestConfig)
.build(); 使用CookieStore实现类似于浏览器Cookie缓存的功能。 可以对每个线程或者每个用户单独设置一个HttpClientContext来实现Http的状态管理。
第四章: HTTP权限验证
UsernamePasswordCredentials定义了基本的username/password的用户证书(适用于标准的HTTP权限验证) NTCredentials适用于windows系统验证 AuthScheme抽象了 面向挑战-应答的权限校验模型,以下是HTTPClient支持的AuthScheme:
Basic: 只需要用户名、密码的验证。 用户名密码明文传输。将该authSScheme和SSL/TLS结合使用可以适应很多场景
Digest: 对用户名、密码做签名,安全性高于Basic
NTLM: windows的那一套,不喜欢windows,不想了解
Kerberos: 一种双向验证的协议,我理解是基于业务层的协议(高于HTTP协议),实现了安全的权限认证服务。
SPNEGO: 这个好像有两个版本,一个版本是Kerberos的扩展版,用于基于Web服务的权限认证协议。 另一个版本是专门给Windows用的 CredentialsProvider: 用户证书生成器,需要用这个类把用户证书和执行上下文结合起来,才能供给HttpClient上层接口使用。 默认实现为: BasicCredentialsProvider
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(
new AuthScope("somehost", AuthScope.ANY_PORT),
new UsernamePasswordCredentials("u1", "p1")); 通过将权限验证的信息结合到执行上下文中(HttpClientContext),可以实现状态查询,以及抢先验证的功能:
CloseableHttpClient httpclient = <...> CredentialsProvider credsProvider = <...>
Lookup<AuthSchemeProvider> authRegistry = <...>
AuthCache authCache = <...> HttpClientContext context = HttpClientContext.create();
context.setCredentialsProvider(credsProvider);
context.setAuthSchemeRegistry(authRegistry);
context.setAuthCache(authCache);
HttpGet httpget = new HttpGet("http://somehost/");
CloseableHttpResponse response1 = httpclient.execute(httpget, context);
<...> AuthState proxyAuthState = context.getProxyAuthState();
System.out.println("Proxy auth state: " + proxyAuthState.getState());
System.out.println("Proxy auth scheme: " + proxyAuthState.getAuthScheme());
System.out.println("Proxy auth credentials: " + proxyAuthState.getCredentials());
AuthState targetAuthState = context.getTargetAuthState();
System.out.println("Target auth state: " + targetAuthState.getState());
System.out.println("Target auth scheme: " + targetAuthState.getAuthScheme());
System.out.println("Target auth credentials: " + targetAuthState.getCredentials()); SPNego Web服务版执行流程:
Client Web Browser does HTTP GET for resource.
Web server returns HTTP 401 status and a header:
WWW-Authenticate: Negotiate
Client generates a
NegTokenInit
, base64 encodes it, and resubmits theGET
with an Authorization header:Authorization: Negotiate <base64 encoding>
.Server decodes the
NegTokenInit
, extracts the supportedMechTypes
(only Kerberos V5 in our case), ensures it is one of the expected ones, and then extracts theMechToken
(Kerberos Token) and authenticates it.If more processing is required another HTTP 401 is returned to the client with more data in the the
WWW-Authenticate
header. Client takes the info and generates another token passing this back in theAuthorization
header until complete.When the client has been authenticated the Web server should return the HTTP 200 status, a final
WWW-Authenticate
header and the page content.
第五章:Fluent API
Fluent适用场景为简单的请求,可以使用流式的代码,避免了用户处理连接相关和资源释放的问题。但是流式调用的基础是Fluent API底层缓存了response到内存中,所以对于response很大的情况可能会消耗
很大的内存,可以通过自定义ResponseHandler来实现更灵活的response处理。 第六章: Http缓存
HttpClient提供兼容Http1.1的缓存机制
当一个缓存版HttpClient执行请求时候执行以下步骤:
1. 检查请求是否符合Http1.1协议
2. 刷新所有与该请求相关的缓存
3. 判断请求是否是可以缓存的,如果不能缓存则请求服务器,并在合适的时候缓存response
4. 如果是命中缓存,则从缓存中读取数据,构建成BasicHttpResponse返回。
5. 如果缓存的结果无法重新验证,则重新请求服务器获取结果 缓存版HttpClient解析response过程:
检查response协议是否兼容
检查response是否是可缓存的
如果是可以缓存的,则读取response,并将数据缓存下来
如果太大无法缓存,则直接返回
使用方法:
CacheConfig cacheConfig = CacheConfig.custom()
.setMaxCacheEntries(1000)
.setMaxObjectSize(8192)
.build();
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(30000)
.setSocketTimeout(30000)
.build();
CloseableHttpClient cachingClient = CachingHttpClients.custom()
.setCacheConfig(cacheConfig)
.setDefaultRequestConfig(requestConfig)
.build(); HttpCacheContext context = HttpCacheContext.create();
HttpGet httpget = new HttpGet("http://www.mydomain.com/content/");
CloseableHttpResponse response = cachingClient.execute(httpget, context);
try {
CacheResponseStatus responseStatus = context.getCacheResponseStatus();
switch (responseStatus) {
case CACHE_HIT:
System.out.println("A response was generated from the cache with " +
"no requests sent upstream");
break;
case CACHE_MODULE_RESPONSE:
System.out.println("The response was generated directly by the " +
"caching module");
break;
case CACHE_MISS:
System.out.println("The response came from an upstream server");
break;
case VALIDATED:
System.out.println("The response was generated from the cache " +
"after validating the entry with the origin server");
break;
}
} finally {
response.close();
}
缓存相关的参数可以通过CacheConfig配置。 默认的缓存介质是放到JVM的内存中,支持的第三方组建EhCache、memcached。支持循环的写入磁盘。 如果都不满足你的需求可以自己实现HttpCacheStorage来实现自己的后端存储方案。 可以实现类似计算机的多级缓存来提升效率
第7章: 高级主题
自定义Http Request格式化(LineFormatter)和response解析(LineParser)
HttpConnectionFactory<HttpRoute, ManagedHttpClientConnection> connFactory =
new ManagedHttpClientConnectionFactory(
new DefaultHttpRequestWriterFactory(),
new DefaultHttpResponseParserFactory(
new MyLineParser(), new DefaultHttpResponseFactory()));
UserTokenHandler(不知道这个的应用场景是什么,官网也没有给什么示例)
FutureRequestExecutionService类似于ExecutorService,将HttpClient(需要支持多线程)和ExecutorService传递进去构建FutureRequestExecutionService,HttpClient支持的线程数和ExecutorService的线程数最好相同,如果HttpClient支持的线程数小于ExecutorService,则该Service无法启动,如果HttpClient的线程数大于ExecutorService,则浪费了部分Connection。
HttpClient httpClient = HttpClientBuilder.create().setMaxConnPerRoute(5).build();
ExecutorService executorService = Executors.newFixedThreadPool(5);
FutureRequestExecutionService futureRequestExecutionService =
new FutureRequestExecutionService(httpClient, executorService);
调度的时候还需要HttpGet、HttpClientContext和ResponseHandler三个参数。 HttpGet是必须的,如果没有它就不知道执行什么请求,HttpClientContext我感觉是为了做一些缓存、统计之类的东西。 通过ResponseHandler定制Task返回的数据。
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(); 以上是通过task.get获取执行结果,还可以通过回调类来实现结果处理:
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()); 任务执行的一些统计信息可以通过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
HttpClient学习记录-系列1(tutorial)的更多相关文章
- HttpClient学习记录-系列2(源码学习)
如何阅读第三方工具源码? 存在多个入口,而且整个类图存在很多孤岛,自上而下的分析策略貌似不行,还是从use case入手,针对单个面分析.难点是如何做范围界定?不至于陷入黑洞 HttpGet -- ...
- UWP学习记录1-开端
UWP学习记录1-开端 1.背景 针对不同基础的人,学习的路线自然是不同的.这篇文章记录的是我个人的学习路线,或者说笔记.我对自己的技术状态的定义是: A.有很好的windows平台编程基础: B.有 ...
- Python学习记录day6
title: Python学习记录day6 tags: python author: Chinge Yang date: 2016-12-03 --- Python学习记录day6 @(学习)[pyt ...
- Dynamic CRM 2013学习笔记 系列汇总
这里列出所有 Dynamic CRM 2013学习笔记 系列文章,方便大家查阅.有任何建议.意见.需要,欢迎大家提交评论一起讨论. 本文原文地址: Dynamic CRM 2013学习笔记 系列汇总 ...
- SQLServer学习笔记系列2
一.写在前面的话 继上一次SQLServer学习笔记系列1http://www.cnblogs.com/liupeng61624/p/4354983.html以后,继续学习Sqlserver,一步一步 ...
- 学习Linux系列--安装Ubuntu
最近学习Linux,使用虚拟机太不方便,于是购买了阿里云最便宜的云主机作为学习设备. 本系列文章记录了个人学习过程的点点滴滴. 学习Linux系列--安装Ubuntu 学习Linux系列--安装软件环 ...
- Dynamic CRM 2015学习笔记 系列汇总
这里列出所有 Dynamic CRM 2015学习笔记 系列文章,方便大家查阅.有任何建议.意见.需要,欢迎大家提交评论一起讨论. 本文原文地址:Dynamic CRM 2015学习笔记 系列汇总 一 ...
- WebService学习笔记系列(二)
soap(简单对象访问协议),它是在http基础之上传递xml格式数据的协议.soap协议分为两个版本,soap1.1和soap1.2. 在学习webservice时我们有一个必备工具叫做tcpmon ...
- 学习Emacs系列教程
emacs最简单入门,只要10分钟 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 3 ...
随机推荐
- solr集群构建的基本流程介绍
先从第一台solr服务器说起:1. 它首先启动一个嵌入式的Zookeeper服务器,作为集群状态信息的管理者,2. 将自己这个节点注册到/node_states/目录下3. 同时将自己注册到/live ...
- HTML语言字符编码
! ! — 惊叹号Exclamation mark ” " " 双引号Quotation mark # # — 数字标志Number sign $ $ — 美元标志Dollar s ...
- iOS ReplayKit实时录制屏幕实现方案的细节记录
项目有个需求,需要把ios设备上的操作画面实时传输出去,也就是类似推流手机直播画面的方案. 一番调研后发现在ios中,我们可以通过ios自带ReplayKit框架实现. 关于ReplayKit的讲解, ...
- python使用mysql
python安装MySQLdb需要ssl,出错,原因如地址: https://stackoverflow.com/questions/46967488/getting-error-403-while- ...
- mysql多实例配置下,用脚本启动mysql时,出现Please read "Security" section of the manual to find out how to run mysqld as root!
[root@localhost 3308]# mysqld stop170414 0:35:28 [Note] --secure-file-priv is set to NULL. Operation ...
- JAVA8 之 Stream sorted() 示例
下面代码以自然序排序一个listlist.stream().sorted() 自然序逆序元素,使用Comparator 提供的reverseOrder() 方法list.stream().sorted ...
- Form 组件的学习
学习链接:http://www.cnblogs.com/haiyan123/p/7778888.html Form组件可以做的几件事情: 1.用户请求数据验证 2.自动生成错误信息 3.打包用户提交的 ...
- 在 Visual Studio 2017 新建的项目中,无法设置项目版本号的通配符规则
错误信息:CS8357 指定的版本字符串中包含与确定性不兼容的通配符.从版本字符串删除通配符,或者禁用此编译的确定性 解决方法:删除项目文件中的配置,或将其设为 False :<Determin ...
- logrus日志使用详解
1.logrus特点 golang标准库的日志框架很简单,logrus框架的特点: 1)完全兼容标准日志库 六种日志级别:debug, info, warn, error, fatal, panic ...
- 科普知识普及 - 桥接VS中继
首先要说明一个很多人理解的误区,中继比桥接好用,真的是这么回事么? 答案是否定的. 我们在说桥接和中继的时候我们要先了解,桥接和中继的工作原理.还有一个问题,估计很多人都想不明白:为什么中继搜到的信号 ...