1.2 HttpClient接口java
HttpClient接口表明了HTTP请求执行最重要的约定.它规定了请求执行过程无任何和限制或者特定的细节以及审阅链接管理,状态管理,认证和重定向处理的实现等细节.这使得装饰接口附加功能更容易好比响应内容的缓存.缓存
一般HttpClient的实现只是做为样子,大量特殊目的的处理器或策略接口的实现来处理HTTP协议具体的各个方面好比重定向或者认证的处理或者决定链接持续的时间.这使得用户能够替换某些默认的实现.安全
ConnectionKeepAliveStrategy keepAliveStrat = new DefaultConnectionKeepAliveStrategy(){ public long getKeepAliveDuration(HttpResponse response, HttpContext context){ long keepAlive = super.getKeepAliveDuration(response,context); if(keepAlive == -1) { //Keep connections alive 5 seconds if keep-alive value //has not be explicitly set by the server keepAlive = 5000; } return keepAlive; } }; CloseableHttpClient httpclient = HttpClients.custom().setKeepAliveStrategy(keepAliveStrat).build();
1.2.1 HttpClient线程安全服务器
HttpClient实现能够认为是线程安全的.推荐对于屡次请求的执行使用该类的同一个实例.session
1.2.2 并发
HttpClient资源的解除ide
当CloseableHttpClient实例不在使用且超出该示例相关的链接管理的做用域是必须经过调用CloseableHttpClient#close()方法关闭.工具
CloseableHttpClient httpclient = HttpClients.createDefault(); try { <...> } finally { httpclient.close(); }
1.3 HTTP执行上下文ui
最初的HTTP被设计成无状态的,响应请求导向协议.然而真实环境中的应用常常须要经过多个逻辑上关联的请求响应交互保存状态信息.为了是程序可以保持处理状态HttpClient容许HTTP请求在特定的上下文中执行.当同一个上下文被一系列的请求复用,逻辑关联的请求将共享一个逻辑上的session.HTTP上下文功能相似于java.util.Map<String,Object>.一个简单的键值对集合.应用程序能够在执行时填充上下文或者在执行完成后检查上下文.线程
HttpContext能够包含任意的对象所以在多个线程共享是不保证线程安全.推荐每一个线程位置本身的上下文.
在HTTP请求执行过程当中HttpClient添加如下上下文属性:
HttpConnection 表明实际抵达目标服务器的链接.
HttpHost 表明链接的目标
HttpRoute 表明完整的链接路由
HttpRequest 表明实际的HTTP请求.最终的HttpRequest对像始终表示消息已被发送到目标服务器的状态.默认的HTTP/1.0和HTTP1.1使用相对请求URI.可是若是请求经过非隧道模式的代理发送则URI是绝对的.
HttpResponse 表明示例的HTTP响应
java.lang.Boolean 表明请求是否彻底的传送到链接的目标.
RequestConfig 表明实际的请求设置.
java.util.List<URI> 表明在请求执行过程当中接收到的全部重定向位置集合
可使用HttpClientContext适配类来简化上下文之间的差别.
HttpContext context = <...>; HttpClientContext clientContext = HttpClientContext.adapt(context); HttpHost target = clientContext.getTargetHost(); HttpRequest request = clientContext.getRequest(); HttpResponse response = clientContext.getResponse(); RequestConfig config = clientContext.getRequestConfig();
表明一个逻辑上相关的会话的请求序列应该用相同的HttpContext实例执行以确保上下文和状态信息在请求间的共享.
在接下来的示例中由初始请求配置将在执行上下文中保持并共享给其余相同上下文中的请求.
CloseableHttpClient httpclient = HttpClients.createDefault(); RequestConfig requestConfig = ReuestConfig.custom().setSocketTimeout(1000).setConnectTimeout(1000).build(); HttpGet httpget1 = new HttpGet(); httpget1.setConfig(requestConfig); CloseableHttpResponse response1 = httpclient.execute(httpget1,context); try { HttpEntity entity1 = response1.getEntity(); } finally { response1.close(); } HttpGet httpget2 = new HttpGet(); httpget2.setConfig(requestConfig); CloseableHttpResponse response1 = httpclient.execute(httpget2,context); try { HttpEntity entity1 = response1.getEntity(); } finally { response1.close(); }
1.4 HTTP协议拦截器
HTTP请求拦截器是HTTP协议具体的常规实现.通常协议拦截预期做用在传入消息一个特定的头部或一组相关的头部.协议拦截器也能够操做消息封装的实体内容-传输内容的压缩和解压是最好的例子.这一般可使用装饰器模式包装原有实体来完成.多个协议拦截器能够被组成一个逻辑单元.
协议拦截器能够经过共享信息合做-好比处理状态-经过HTTP执行上下文.协议拦截器能够存储一个或一系列请求的处理状态.一般这种拦截器的执行顺序并不重要,只要他们不依赖执行上下文的状态.若是协议拦截具备相互依赖关系而必须以特定顺序执行,则应保证添加的顺序与预期执行的顺序一致.
协议拦截器必须为线程安全的实现.与servlet相似,协议拦截器不该该使用实例变量除非对这些变量进行同步.
下面的例子展现上下文如何用于维持一系列请求的处理状态:
CloseableHttpClient httpclient = HttpClients.custom().addInterceptorLast(new HttpRequestInterceptor(){ public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException{ AtomicInteger count = (AtomicInteger)context.getAttribute("count"); } }).build(); AtomicInteger count = new AtomicInteger(1); HttpClientContext localContext = HttpClientContext.create(); localContext.setAttribute("count",count); HttpGet httpget = new HttpGer(""); for(int i = 0; i < 10; i++){ CloseableHttpResponse response = httpclient.execute(httpget, localContext); try{ HttpEntity entity = response.getEntity(); } finally { response.close(); } }
1.5 异常处理
HTTP协议的处理这可能抛出两种类型的异常:java.io.IOException 表示I/O失败好比套接字超时或套接字重置,HttpException表示HTTP失败好比违背了HTTP协议.一般I/O错误是非致命和可恢复,然而HTTP协议错误是致命的且没法自动恢复.请注意HttpClient的实现会从新用java.io.IOException的子类ClientProtocolException抛出HttpException.用户能够在同一个catch代码块中处理I/O错误和协议错误.
1.5.1 HTTP传输安全
HTTP协议并不适合全部类型的应用。HTTP 是一个面向简单的请求/响应协议,最初设计来支持静态或动态生成的内容检索。HTTP从未有意支持事务操做。好比,HTTP服务器将考虑其合同履行的一部分,若是它成功地接收和处理请求,生成一个响应并发送状态码至客户端。服务器不会试图回滚事务当客户端因读取超时、请求取消或系统故障而接收响应实体失败。若是客户端决定重试相同的请求,服务器最终将不可避免地屡次执行相同的事务。在某些状况下,这可能会致使应用程序数据损坏或不一致的应用程序状态。
尽管HTTP从未被设计成支持事务处理,它仍然知足做为关键任务应用程序传输协议所需知足的条件。
1.5.2 幂等方法
1.5.3 异常自动恢复
默认的HttpClient试图自动从I/O异常中恢复.默认的自动恢复机制只能处理一小部分异常.
HttpClient不会尝试从任何逻辑或HTTP协议错误中恢复(继承自HttpException类).
HttpClient将自动重试被认定的幂等方法.
HttpClient将自动重试当HTTP请求仍然在传输到目标服务器失败的方法(好比请求尚未彻底传输到服务器).
1.5.4 请求重试处理方法
HttpRequestRetryHandler接口的实现用于自定义异常回复机制.
HttpRequestRetryHandler myRetryHandler = new HttpRequestRetryHandler(){ public boolean retryRequest(IOException exception, int executionCouont, HttpContext context){ if(executionCount >= 5){ return false; } if(exception instanceof InterruptedIOException){ return false; } if(exception instanceof UnknownHostException){ return false; } if(exception instanceof ConnecTimeoutException){ return false; } if(exception instanceof SSLException){ return false; } HttpClientContext clientContext = HttpClientContext.adapt(context); HttpRequest request = clientContext.getRequest(); boolean idmpotent - !(request instanceof HttpEntityEnclosingRequest); if(idempotent){ return true; } return false; } }; CloseableHttpClient httpclient = HttpClients.custom().setRetryHandler(myRetryHandler).build();
请注意可使用StandardHttpRequestRetryHandler替代默认配置以便RFC-2616定义的幂等方法能够安全的自动重试:GET,HEAD,PUT,DELTE,OPTIONS和TRACE.
1.6 终止请求
在某些状况下HTTP请求可能因为目标服务器高负荷或者客户端有太多的请求在使用致使请求在预期的时间范围内执行失败.在这种状况下可能有必要提早终止该请求并解除执行线程对I/O操做的阻塞.经过HttpClient执行的HTTP请求能够在执行的任何阶段调用HttpUriRequest#abort()方法终止.该方法是线程安全的能够从任何线程调用.当HTTP请求终止执行现场-即便当前阻塞的I/O操做-经过抛出InterruptedIOException保证解除.
1.7 重定向处理
HttpClient自动处理全部类型的重定向,除了那些HTTP规范要求必须用户介入.POST和PUT请求的see Other(状态code 303)重定向按HTTP规范的要求转换成GET请求.能够自定义重定向策略覆盖HTTP规范规定的方式.
LaxRedirectStrategy redirectStrategy = new LaxRedirectStrategy(); CloseableHttpClient httpclient = HttpClients.custom().setRedirectStrategy(redirectStrategy).build();
HttpClient常常须要在执行过程当中重写请求信息.默认的HTTP/1.0和HTTP/1.1一般使用相对请求URIS.一样,原始的请求也可能从其余位置重定向屡次.最总的绝对HTTP位置可以使用原始的请求和上下文得到.工具方法URIUtils#resolve能够用来解释绝对URI用于最终的请求.该方法包括重定向请求或原始请求的最后一个片断标识符.
CloseableHttpClient httpclient = HttpClients.createDefault(); HttpClientContext context = HttpClientContext.create(); HttpGet httpget = new HttpGet(); CloseableHttpResponse response = httpclient.execute(httpget,context); try{ HttpPost target = context.getTargetHost(); List<URI> redirectLocations = context.getRedirectLocations(); URI location = URIUtils.resolve(httpget.getURI(),target,redirectLocations); System.out.println("Final HTTP location: " + location.toASCIIString()); } finally { response.close(); }