摘要:自定義是請求響應式的,本是無狀態的,不過應用通常需要在幾個連續的請求之間保持聯系,因此可以使用這個來傳遞變量,注意這個不是線程安全的,建議每個線程使用一個。這個方法是線程安全的,而且可以從任意線程中調用。協議攔截器必須實現為線程安全的。
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 * {@link 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只能讀取一次。
表單:
Listformparams = new ArrayList (); 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
最直接的方式是使用ResponseHandler,它不需要去自己管理連接和資源釋放,HttpClient會自動去保證連接被釋放不論是否發生異常。
CloseableHttpClient httpclient = HttpClients.createDefault(); HttpGet httpget = new HttpGet("http://localhost/json"); ResponseHandler5、自定義HttpClientrh = new ResponseHandler () { @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);
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(); ListredirectLocations = 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(); }
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/65882.html
摘要:連接不是線程安全的,每次只能在一個線程里頭使用,通過來管理。主要通過來作為代理類,管理連接的狀態和操作。如果底層的連接被關閉了,則它會歸還到。建議每個線程維護自己的 持久連接 通常一次連接之間的握手還是很耗費時間的,Http1.1提供了持久連接,可以在一次連接期間,發送多次請求。 HttpClientConnectionManager Http連接不是線程安全的,每次只能在一個線程里頭...
摘要:引入了新的環境和概要信息,是一種更揭秘與實戰六消息隊列篇掘金本文,講解如何集成,實現消息隊列。博客地址揭秘與實戰二數據緩存篇掘金本文,講解如何集成,實現緩存。 Spring Boot 揭秘與實戰(九) 應用監控篇 - HTTP 健康監控 - 掘金Health 信息是從 ApplicationContext 中所有的 HealthIndicator 的 Bean 中收集的, Spring...
摘要:個人前端文章整理從最開始萌生寫文章的想法,到著手開始寫,再到現在已經一年的時間了,由于工作比較忙,更新緩慢,后面還是會繼更新,現將已經寫好的文章整理一個目錄,方便更多的小伙伴去學習。 showImg(https://segmentfault.com/img/remote/1460000017490740?w=1920&h=1080); 個人前端文章整理 從最開始萌生寫文章的想法,到著手...
摘要:新手篇入門基礎教程關于的分享此前一直都是零零散散的想到什么就寫什么,整體寫的比較亂吧。上兩周寫的五篇內容,匯總到一起就算是新手入門的一個基礎性教程吧持續更新中。應該在改版完成后就可以正常申請下載了。 Hadoop新手篇:hadoop入門基礎教程關于hadoop的分享此前一直都是零零散散的想到什么就寫什么,整體寫的比較亂吧。最近可能還算好的吧,畢竟花了兩周的時間詳細的寫完的了hadoop...
閱讀 582·2021-11-22 14:45
閱讀 3070·2021-10-15 09:41
閱讀 1555·2021-10-11 10:58
閱讀 2797·2021-09-04 16:45
閱讀 2606·2021-09-03 10:45
閱讀 3238·2019-08-30 15:53
閱讀 1221·2019-08-29 12:28
閱讀 2133·2019-08-29 12:14