1、关闭流和response- CloseableHttpClient httpclient = HttpClients.createDefault();
- HttpGet httpget = new HttpGet("http://localhost/");
- CloseableHttpResponse response = httpclient.execute(httpget);
- try {
- HttpEntity entity = response.getEntity();
- if (entity != null) {
- InputStream instream = entity.getContent();
- try {
- // do something useful
- } finally {
- instream.close();
- }
- }
- } finally {
- response.close();
- }
复制代码The difference between closing the content stream and closing the response is that the former will attempt to keep the underlying connection alive by consuming the entity content while the latter immediately shuts down and discards the connection.
关闭InputStream和关闭response的区别在于:关闭InputStream会通过消费实体内容与底层保持连接,而关闭response则会立即停止和丢弃连接。 - class HttpResponseProxy implements CloseableHttpResponse {
- private final HttpResponse original;
- private final ConnectionHolder connHolder;
- public HttpResponseProxy(final HttpResponse original, final ConnectionHolder connHolder) {
- this.original = original;
- this.connHolder = connHolder;
- ResponseEntityProxy.enchance(original, connHolder);
- }
- public void close() throws IOException {
- if (this.connHolder != null) {
- this.connHolder.abortConnection();
- }
- }
复制代码丢弃的代码: - public void abortConnection() {
- synchronized (this.managedConn) {
- if (this.released) {
- return;
- }
- this.released = true;
- try {
- this.managedConn.shutdown();
- log.debug("Connection discarded");
- } catch (final IOException ex) {
- if (this.log.isDebugEnabled()) {
- this.log.debug(ex.getMessage(), ex);
- }
- } finally {
- this.manager.releaseConnection(
- this.managedConn, null, 0, TimeUnit.MILLISECONDS);
- }
- }
- }
复制代码 2、读取Entity
如果是使用EntityUtils#consume(HttpEntity)这个方法,它会自动去关闭inputStream
如果不需要读取全部的实体,则可以直接关闭response,来终止InputSteam的读取(关闭response会自动关闭InputStream)。 - CloseableHttpClient httpclient = HttpClients.createDefault();
- HttpGet httpget = new HttpGet("http://localhost/");
- CloseableHttpResponse response = httpclient.execute(httpget);
- try {
- HttpEntity entity = response.getEntity();
- if (entity != null) {
- InputStream instream = entity.getContent();
- int byteOne = instream.read();
- int byteTwo = instream.read();
- // Do not need the rest
- }
- } finally {
- response.close();
- }
复制代码不推荐使用EntityUtils的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();
- }
复制代码因为toString方法默认最大的长度是Integer.MAX_VALUE,这个有点危险,会导致缓冲区溢出。 - /**
- * Get the entity content as a String, using the provided default character set
- * if none is found in the entity.
- * If defaultCharset is null, the default "ISO-8859-1" is used.
- *
- * @param entity must not be null
- * @param defaultCharset character set to be applied if none found in the entity
- * @return the entity content as a String. May be null if
- * {[url=home.php?mod=space&uid=17823]@LINK[/url] HttpEntity#getContent()} is null.
- * @throws ParseException if header elements cannot be parsed
- * @throws IllegalArgumentException if entity is null or if content length > Integer.MAX_VALUE
- * @throws IOException if an error occurs reading the input stream
- * @throws UnsupportedCharsetException Thrown when the named charset is not available in
- * this instance of the Java virtual machine
- */
- public static String toString(
- final HttpEntity entity, final Charset defaultCharset) throws IOException, ParseException {
- Args.notNull(entity, "Entity");
- final InputStream instream = entity.getContent();
- if (instream == null) {
- return null;
- }
- try {
- Args.check(entity.getContentLength() <= Integer.MAX_VALUE,
- "HTTP entity too large to be buffered in memory");
- int i = (int)entity.getContentLength();
- if (i < 0) {
- i = 4096;
- }
- Charset charset = null;
- try {
- final ContentType contentType = ContentType.get(entity);
- if (contentType != null) {
- charset = contentType.getCharset();
- }
- } catch (final UnsupportedCharsetException ex) {
- throw new UnsupportedEncodingException(ex.getMessage());
- }
- if (charset == null) {
- charset = defaultCharset;
- }
- if (charset == null) {
- charset = HTTP.DEF_CONTENT_CHARSET;
- }
- final Reader reader = new InputStreamReader(instream, charset);
- final CharArrayBuffer buffer = new CharArrayBuffer(i);
- final char[] tmp = new char[1024];
- int l;
- while((l = reader.read(tmp)) != -1) {
- buffer.append(tmp, 0, l);
- }
- return buffer.toString();
- } finally {
- instream.close();
- }
- }
复制代码如果entity需要反复使用的话,最好使用缓存起来: - CloseableHttpResponse response = <...>
- HttpEntity entity = response.getEntity();
- if (entity != null) {
- entity = new BufferedHttpEntity(entity);
- }
复制代码 3、写Entity
主要有四种:StringEntity, ByteArrayEntity, InputStreamEntity,和FileEntity - File file = new File("somefile.txt");
- FileEntity entity = new FileEntity(file,
- ContentType.create("text/plain", "UTF-8"));
- HttpPost httppost = new HttpPost("http://localhost/action.do");
- httppost.setEntity(entity);
复制代码注意:InputStreamEnity只能读取一次。
表单: - List<NameValuePair> formparams = new ArrayList<NameValuePair>();
- formparams.add(new BasicNameValuePair("param1", "value1"));
- formparams.add(new BasicNameValuePair("param2", "value2"));
- UrlEncodedFormEntity entity = new UrlEncodedFormEntity(formparams, Consts.UTF_8);
- HttpPost httppost = new HttpPost("http://localhost/handler.do");
- httppost.setEntity(entity);
复制代码它会自动对参数进行URL编码。 param1=value1¶m2=value2
4、处理Response
最直接的方式是使用ResponseHandler,它不需要去自己管理连接和资源释放,HttpClient会自动去保证连接被释放不论是否发生异常。 - CloseableHttpClient httpclient = HttpClients.createDefault();
- HttpGet httpget = new HttpGet("http://localhost/json");
- ResponseHandler<MyJsonObject> rh = new ResponseHandler<MyJsonObject>() {
- @Override
- public JsonObject handleResponse(
- final HttpResponse response) throws IOException {
- StatusLine statusLine = response.getStatusLine();
- HttpEntity entity = response.getEntity();
- if (statusLine.getStatusCode() >= 300) {
- throw new HttpResponseException(
- statusLine.getStatusCode(),
- statusLine.getReasonPhrase());
- }
- if (entity == null) {
- throw new ClientProtocolException("Response contains no content");
- }
- Gson gson = new GsonBuilder().create();
- ContentType contentType = ContentType.getOrDefault(entity);
- Charset charset = contentType.getCharset();
- Reader reader = new InputStreamReader(entity.getContent(), charset);
- return gson.fromJson(reader, MyJsonObject.class);
- }
- };
- MyJsonObject myjson = client.execute(httpget, rh);
复制代码 5、自定义HttpClient- ConnectionKeepAliveStrategy keepAliveStrat = new DefaultConnectionKeepAliveStrategy() {
- @Override
- public long getKeepAliveDuration(
- HttpResponse response,
- HttpContext context) {
- long keepAlive = super.getKeepAliveDuration(response, context);
- if (keepAlive == -1) {
- // Keep connections alive 5 seconds if a keep-alive value
- // has not be explicitly set by the server
- keepAlive = 5000;
- }
- return keepAlive;
- }
- };
- CloseableHttpClient httpclient = HttpClients.custom()
- .setKeepAliveStrategy(keepAliveStrat)
- .build();
复制代码 6、HttpContext
Http是请求响应式的,本是无状态的,不过web应用通常需要在几个连续的请求之间保持联系,因此可以使用这个来传递变量,注意这个不是线程安全的,建议每个线程使用一个。 - CloseableHttpClient httpclient = HttpClients.createDefault();
- RequestConfig requestConfig = RequestConfig.custom()
- .setSocketTimeout(1000)
- .setConnectTimeout(1000)
- .build();
- HttpGet httpget1 = new HttpGet("http://localhost/1");
- httpget1.setConfig(requestConfig);
- CloseableHttpResponse response1 = httpclient.execute(httpget1, context);
- try {
- HttpEntity entity1 = response1.getEntity();
- } finally {
- response1.close();
- }
- HttpGet httpget2 = new HttpGet("http://localhost/2");
- CloseableHttpResponse response2 = httpclient.execute(httpget2, context);
- try {
- HttpEntity entity2 = response2.getEntity();
- } finally {
- response2.close();
- }
复制代码两次请求,通过context共享配置。
7、重试机制- HttpRequestRetryHandler myRetryHandler = new HttpRequestRetryHandler() {
- public boolean retryRequest(
- IOException exception,
- int executionCount,
- HttpContext context) {
- if (executionCount >= 5) {
- // Do not retry if over max retry count
- return false;
- }
- if (exception instanceof InterruptedIOException) {
- // Timeout
- return false;
- }
- if (exception instanceof UnknownHostException) {
- // Unknown host
- return false;
- }
- if (exception instanceof ConnectTimeoutException) {
- // Connection refused
- return false;
- }
- if (exception instanceof SSLException) {
- // SSL handshake exception
- return false;
- }
- HttpClientContext clientContext = HttpClientContext.adapt(context);
- HttpRequest request = clientContext.getRequest();
- boolean idempotent = !(request instanceof HttpEntityEnclosingRequest);
- if (idempotent) {
- // Retry if the request is considered idempotent
- return true;
- }
- return false;
- }
- };
- CloseableHttpClient httpclient = HttpClients.custom()
- .setRetryHandler(myRetryHandler)
- .build();
复制代码如果是幂等的操作,则可以重试。
8、终止请求
在一些情况下,由于目标服务器的高负载或客户端有很多活动的请求,那么 HTTP 请求执行会在预期的时间框内而失败。这时,就可能不得不过早地中止请求,解除封锁在 I/O 执行中的线程封锁。被 HttpClient 执行的 HTTP 请求可以在执行的任意阶段通过调用HttpUriRequest#abort()方法而中止。这个方法是线程安全的,而且可以从任意线程中调用。当一个 HTTP 请求被中止时,它的执行线程就封锁在 I/O 操作中了,而且保证通过抛出 InterruptedIOException 异常来解锁。
9、协议拦截器
协议拦截器可以使用 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");
- request.addHeader("Count", Integer.toString(count.getAndIncrement()));
- }
- })
- .build();
- AtomicInteger count = new AtomicInteger(1);
- HttpClientContext localContext = HttpClientContext.create();
- localContext.setAttribute("count", count);
- HttpGet httpget = new HttpGet("http://localhost/");
- for (int i = 0; i < 10; i++) {
- CloseableHttpResponse response = httpclient.execute(httpget, localContext);
- try {
- HttpEntity entity = response.getEntity();
- } finally {
- response.close();
- }
- }
复制代码 10、重定向处理
HttpClient会自动处理重定向,当然也可以自己自定义策略。 - LaxRedirectStrategy redirectStrategy = new LaxRedirectStrategy();
- CloseableHttpClient httpclient = HttpClients.custom()
- .setRedirectStrategy(redirectStrategy)
- .build();
复制代码resolve可以用来构建绝对路径: - CloseableHttpClient httpclient = HttpClients.createDefault();
- HttpClientContext context = HttpClientContext.create();
- HttpGet httpget = new HttpGet("http://localhost:8080/");
- CloseableHttpResponse response = httpclient.execute(httpget, context);
- try {
- HttpHost target = context.getTargetHost();
- List<URI> redirectLocations = context.getRedirectLocations();
- URI location = URIUtils.resolve(httpget.getURI(), target, redirectLocations);
- System.out.println("Final HTTP location: " + location.toASCIIString());
- // Expected to be an absolute URI
- } finally {
- response.close();
- }
复制代码 |