摘要:自定義是請(qǐng)求響應(yīng)式的,本是無(wú)狀態(tài)的,不過(guò)應(yīng)用通常需要在幾個(gè)連續(xù)的請(qǐng)求之間保持聯(lián)系,因此可以使用這個(gè)來(lái)傳遞變量,注意這個(gè)不是線程安全的,建議每個(gè)線程使用一個(gè)。這個(gè)方法是線程安全的,而且可以從任意線程中調(diào)用。協(xié)議攔截器必須實(shí)現(xiàn)為線程安全的。
1、關(guān)閉流和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.
關(guān)閉InputStream和關(guān)閉response的區(qū)別在于:關(guān)閉InputStream會(huì)通過(guò)消費(fèi)實(shí)體內(nèi)容與底層保持連接,而關(guān)閉response則會(huì)立即停止和丟棄連接。
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)這個(gè)方法,它會(huì)自動(dòng)去關(guān)閉inputStream
如果不需要讀取全部的實(shí)體,則可以直接關(guān)閉response,來(lái)終止InputSteam的讀?。P(guān)閉response會(huì)自動(dòng)關(guān)閉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方法,如果需要的話,最好自己判斷長(zhǎng)度:
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(); }
因?yàn)閠oString方法默認(rèn)最大的長(zhǎng)度是Integer.MAX_VALUE,這個(gè)有點(diǎn)危險(xiǎn),會(huì)導(dǎo)致緩沖區(qū)溢出。
/** * 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需要反復(fù)使用的話,最好使用緩存起來(lái):
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);
它會(huì)自動(dòng)對(duì)參數(shù)進(jìn)行URL編碼。
param1=value1¶m2=value2
最直接的方式是使用ResponseHandler,它不需要去自己管理連接和資源釋放,HttpClient會(huì)自動(dòng)去保證連接被釋放不論是否發(fā)生異常。
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是請(qǐng)求響應(yīng)式的,本是無(wú)狀態(tài)的,不過(guò)web應(yīng)用通常需要在幾個(gè)連續(xù)的請(qǐng)求之間保持聯(lián)系,因此可以使用這個(gè)來(lái)傳遞變量,注意這個(gè)不是線程安全的,建議每個(gè)線程使用一個(gè)。
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(); }
兩次請(qǐng)求,通過(guò)context共享配置。
7、重試機(jī)制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、終止請(qǐng)求在一些情況下,由于目標(biāo)服務(wù)器的高負(fù)載或客戶端有很多活動(dòng)的請(qǐng)求,那么 HTTP 請(qǐng)求執(zhí)行會(huì)在預(yù)期的時(shí)間框內(nèi)而失敗。這時(shí),就可能不得不過(guò)早地中止請(qǐng)求,解除封鎖在 I/O 執(zhí)行中的線程封鎖。被 HttpClient 執(zhí)行的 HTTP 請(qǐng)求可以在執(zhí)行的任意階段通過(guò)調(diào)用HttpUriRequest#abort()方法而中止。這個(gè)方法是線程安全的,而且可以從任意線程中調(diào)用。當(dāng)一個(gè) HTTP 請(qǐng)求被中止時(shí),它的執(zhí)行線程就封鎖在 I/O 操作中了,而且保證通過(guò)拋出 InterruptedIOException 異常來(lái)解鎖。
9、協(xié)議攔截器協(xié)議攔截器可以使用 HTTP 內(nèi)容來(lái)為一個(gè)或多個(gè)連續(xù)的請(qǐng)求存儲(chǔ)一個(gè)處理狀態(tài)。 協(xié)議攔截器必須實(shí)現(xiàn)為線程安全的。和 Servlet 相似,協(xié)議攔截器不應(yīng)該使用實(shí)例變量,除非訪問(wèn)的那些變量是同步的。
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會(huì)自動(dòng)處理重定向,當(dāng)然也可以自己自定義策略。
LaxRedirectStrategy redirectStrategy = new LaxRedirectStrategy(); CloseableHttpClient httpclient = HttpClients.custom() .setRedirectStrategy(redirectStrategy) .build();
resolve可以用來(lái)構(gòu)建絕對(duì)路徑:
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(); }
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/65882.html
摘要:連接不是線程安全的,每次只能在一個(gè)線程里頭使用,通過(guò)來(lái)管理。主要通過(guò)來(lái)作為代理類,管理連接的狀態(tài)和操作。如果底層的連接被關(guān)閉了,則它會(huì)歸還到。建議每個(gè)線程維護(hù)自己的 持久連接 通常一次連接之間的握手還是很耗費(fèi)時(shí)間的,Http1.1提供了持久連接,可以在一次連接期間,發(fā)送多次請(qǐng)求。 HttpClientConnectionManager Http連接不是線程安全的,每次只能在一個(gè)線程里頭...
摘要:引入了新的環(huán)境和概要信息,是一種更揭秘與實(shí)戰(zhàn)六消息隊(duì)列篇掘金本文,講解如何集成,實(shí)現(xiàn)消息隊(duì)列。博客地址揭秘與實(shí)戰(zhàn)二數(shù)據(jù)緩存篇掘金本文,講解如何集成,實(shí)現(xiàn)緩存。 Spring Boot 揭秘與實(shí)戰(zhàn)(九) 應(yīng)用監(jiān)控篇 - HTTP 健康監(jiān)控 - 掘金Health 信息是從 ApplicationContext 中所有的 HealthIndicator 的 Bean 中收集的, Spring...
摘要:個(gè)人前端文章整理從最開始萌生寫文章的想法,到著手開始寫,再到現(xiàn)在已經(jīng)一年的時(shí)間了,由于工作比較忙,更新緩慢,后面還是會(huì)繼更新,現(xiàn)將已經(jīng)寫好的文章整理一個(gè)目錄,方便更多的小伙伴去學(xué)習(xí)。 showImg(https://segmentfault.com/img/remote/1460000017490740?w=1920&h=1080); 個(gè)人前端文章整理 從最開始萌生寫文章的想法,到著手...
摘要:新手篇入門基礎(chǔ)教程關(guān)于的分享此前一直都是零零散散的想到什么就寫什么,整體寫的比較亂吧。上兩周寫的五篇內(nèi)容,匯總到一起就算是新手入門的一個(gè)基礎(chǔ)性教程吧持續(xù)更新中。應(yīng)該在改版完成后就可以正常申請(qǐng)下載了。 Hadoop新手篇:hadoop入門基礎(chǔ)教程關(guān)于hadoop的分享此前一直都是零零散散的想到什么就寫什么,整體寫的比較亂吧。最近可能還算好的吧,畢竟花了兩周的時(shí)間詳細(xì)的寫完的了hadoop...
閱讀 661·2021-11-22 14:45
閱讀 3161·2021-10-15 09:41
閱讀 1711·2021-10-11 10:58
閱讀 2862·2021-09-04 16:45
閱讀 2681·2021-09-03 10:45
閱讀 3300·2019-08-30 15:53
閱讀 1273·2019-08-29 12:28
閱讀 2206·2019-08-29 12:14