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: NegotiateClient generates a
NegTokenInit, base64 encodes it, and resubmits theGETwith 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-Authenticateheader. Client takes the info and generates another token passing this back in theAuthorizationheader until complete.When the client has been authenticated the Web server should return the HTTP 200 status, a final
WWW-Authenticateheader 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 ...
随机推荐
- Linux之网络编程:时间服务器
基于TCP-服务器 1,创建一个socket套接字 int socket(int domain,int type,int protocol) domain:IP地址族,AF_INET(IPv4).AF ...
- 使用python实现深度神经网络 1(转)
使用python实现深度神经网络 1(转) https://blog.csdn.net/oxuzhenyi/article/details/73026790
- 转发:entos7修改文件夹权限和用户名用户组
Linux系统下经常遇到文件或者文件夹的权限问题,或者是因为文件夹所属的用户问题而没有访问的权限.根据我自己遇到的情况,对这类问题做一个小结.在命令行使用命令“ll”或者“ls -a”,可以查看文件或 ...
- Linux学习必备
17,继往开来 实践是检验真理的唯一标准! ---运维技术组----mvpbang #开源代码 https://github.com/ #目前最受欢迎的 https://gitee.com/ ...
- windows10开启wst子系统
需求描述: 在玩docker发现需要linux运行玩转,直接在vmware虚拟机上跑 ,性能有损耗.想直接在windows下运行docker 问题解决: windows10的wst子系统可以安装lin ...
- CentOS 7.4nginx配置SSL
一.在/etc/nginx/conf.d目录下创建虚拟主机配置文件 server { listen 80; server_name www.xx.com xx.com; return 301 http ...
- 减少网站跳转时间,增强网站数据安全——HSTS 详解
近年来随着 Google.Apple.百度等公司不断推动 HTTPS 普及,全网 HTTPS 已是大势所趋.目前多数网站都已经支持 HTTPS 访问,但是在由 HTTP 转向 HTTPS 路程中,不少 ...
- 【iCore1S 双核心板_ARM】例程十七:FSMC实验——读写FPGA
实验现象: 先烧写FPGA程序,再烧写ARM程序,ARM程序烧写完毕后即开始读写RAM测试,测试成功,绿色ARM·LED亮,测试失败,红色ARM·LED闪烁. 核心代码: int main(void) ...
- 【转载】基于rasa的对话系统搭建(上)
文章介绍使用rasa nlu和 rasa core 实现一个电信领域对话系统demo,实现简单的业务查询办理功能,更完善的实现需要进一步数据的收集.demo基于玩具数据集,可以达到下面的效果: Bot ...
- flume的安装部署
系统环境:centos7.5 64位系统 1.下载安装包 官网下载离线安装包:apache-flume-1.8.0-bin.tar.gz 也可以使用linux命令下载: wget -c http:/ ...