1、httpclient總結:
一、基本知識准備
(1)構建URI工具類,URIBuilder
(2)HttpResponse類,可以添加Header信息
獲取所有Header信息的方法,調用HeaderIterator接口
HeaderIterator it = response.headerIterator("Set-Cookie"); while (it.hasNext()) { System.out.println(it.next()); }
(3)HttpEntity內容實體 可以被裝入Request和Response中..
只有獨立的entity才可以被重復調用.
當請求需要entity HttpEntity.writeTo(OutputStream)
從響應中解析entity HttpEntity.getContent()
HttpEntity.getContentType()
HttpEntity.getContentLength()
HttpEntity.getContentEncoding()
對entity進行解析可采用流的方式或者調用EntityUtils,但后者有長度的限制2048
利用 BufferedEntity可以將entity緩存到本地磁盤,用來進行多次讀取.
創建entity信息時需要指定meta信息,包括contentType
(4)可以調用ResponseHandler寫入響應統一處理
二、常用策略
keep-Alieve策略:自定義ConnectionKeepAliveStrategy
重定向策略:LaxRedirectStrategy
三、資源分配
當CloseableHttpClient不再需要,並且不再連接管理的范圍,需要調用CloseableHttpClient.close()方法將其關閉..
四、HttpClient狀態管理
1、在HTTP上下文中,很多有邏輯關系的請求都可以放入到同一個session中..
HttpClient本身線程HttpContext 包含任意的鍵值對,因此線程不安全..通常建議每個線程擁有自己的上下文
2、自動恢復機制---->HttpRequestRetryHandler
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();
4、多線程中的應用AtomicInteger,,,,待研究...
從連接管理器中獲取連接 (1)通過HttpClientConnectionManager來管理一個連接 HttpClientContext context = HttpClientContext.create(); HttpClientConnectionManager connMrg = new BasicHttpClientConnectionManager(); HttpRoute route = new HttpRoute(new HttpHost("www.yeetrack.com", 80)); // 獲取新的連接. 這里可能耗費很多時間 ConnectionRequest connRequest = connMrg.requestConnection(route, null); // 10秒超時 HttpClientConnection conn = connRequest.get(10, TimeUnit.SECONDS); try { // 如果創建連接失敗 if (!conn.isOpen()) { // establish connection based on its route info connMrg.connect(conn, route, 1000, context); // and mark it as route complete connMrg.routeComplete(conn, route, context); } // 進行自己的操作. } finally { connMrg.releaseConnection(conn, null, 1, TimeUnit.MINUTES); } 通過更復雜的PoolingHttpClientConnectionManager來管理多個連接,適合多線程中的請求 PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(); // 將最大連接數增加到200 cm.setMaxTotal(200); // 將每個路由基礎的連接增加到20 cm.setDefaultMaxPerRoute(20); //將目標主機的最大連接數增加到50 HttpHost localhost = new HttpHost("www.yeetrack.com", 80); cm.setMaxPerRoute(new HttpRoute(localhost), 50); CloseableHttpClient httpClient = HttpClients.custom() .setConnectionManager(cm) .build(); 示例1--------------------------------- PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(); CloseableHttpClient httpClient = HttpClients.custom() .setConnectionManager(cm) .build(); // URL列表數組 String[] urisToGet = { "http://www.domain1.com/", "http://www.domain2.com/", "http://www.domain3.com/", "http://www.domain4.com/" }; // 為每個url創建一個線程,GetThread是自定義的類 GetThread[] threads = new GetThread[urisToGet.length]; for (int i = 0; i < threads.length; i++) { HttpGet httpget = new HttpGet(urisToGet[i]); threads[i] = new GetThread(httpClient, httpget); } // 啟動線程 for (int j = 0; j < threads.length; j++) { threads[j].start(); } // join the threads for (int j = 0; j < threads.length; j++) { threads[j].join(); } 自定義類GetThread static class GetThread extends Thread { private final CloseableHttpClient httpClient; private final HttpContext context; private final HttpGet httpget; public GetThread(CloseableHttpClient httpClient, HttpGet httpget) { this.httpClient = httpClient; this.context = HttpClientContext.create(); this.httpget = httpget; } @Override public void run() { try { CloseableHttpResponse response = httpClient.execute( httpget, context); try { HttpEntity entity = response.getEntity(); } finally { response.close(); } } catch (ClientProtocolException ex) { // Handle protocol errors } catch (IOException ex) { // Handle I/O errors } } }
注意:即使httpclient可以被多線程訪問,仍建議每個httpclient采用自己的context
5、 public static class IdleConnectionMonitorThread extends Thread { private final HttpClientConnectionManager connMgr; private volatile boolean shutdown; public IdleConnectionMonitorThread(HttpClientConnectionManager connMgr) { super(); this.connMgr = connMgr; } @Override public void run() { try { while (!shutdown) { synchronized (this) { wait(5000); // 關閉失效的連接 connMgr.closeExpiredConnections(); // 可選的, 關閉30秒內不活動的連接 connMgr.closeIdleConnections(30, TimeUnit.SECONDS); } } } catch (InterruptedException ex) { // terminate } } public void shutdown() { shutdown = true; synchronized (this) { notifyAll(); } } }