标签:
1.2 HttpClient接口
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实现可以认为是线程安全的.推荐对于多次请求的执行使用该类的同一个实例.
1.2.2
HttpClient资源的解除
当CloseableHttpClient实例不在使用且超出该示例相关的连接管理的作用域是必须通过调用CloseableHttpClient#close()方法关闭.
CloseableHttpClient httpclient = HttpClients.createDefault(); try { <...> } finally { httpclient.close(); }
1.3 HTTP执行上下文
最初的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传输安全
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(); }
标签:
原文地址:http://my.oschina.net/yaoliyc/blog/526475