為什么需要使用http連接池
1、降低延遲:如果不采用連接池,每次連接發起Http請求的時候都會重新建立TCP連接(經歷3次握手),用完就會關閉連接(4次揮手),如果采用連接池則減少了這部分時間損耗,別小看這幾次握手,本人經過測試發現,基本上3倍的時間延遲
2、支持更大的並發:如果不采用連接池,每次連接都會打開一個端口,在大並發的情況下系統的端口資源很快就會被用完,導致無法建立新的連接
連接池實例
連接池管理器代碼
import org.apache.http.config.Registry; import org.apache.http.config.RegistryBuilder; import org.apache.http.conn.socket.ConnectionSocketFactory; import org.apache.http.conn.socket.LayeredConnectionSocketFactory; import org.apache.http.conn.socket.PlainConnectionSocketFactory; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import javax.annotation.PostConstruct; import javax.net.ssl.SSLContext; import java.security.NoSuchAlgorithmException; public class HttpConnectionManager { PoolingHttpClientConnectionManager cm = null; @PostConstruct public void init() { LayeredConnectionSocketFactory sslsf = null; try { sslsf = new SSLConnectionSocketFactory(SSLContext.getDefault()); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory> create() .register("https", sslsf) .register("http", new PlainConnectionSocketFactory()) .build(); cm =new PoolingHttpClientConnectionManager(socketFactoryRegistry); cm.setMaxTotal(200); cm.setDefaultMaxPerRoute(20); } public CloseableHttpClient getHttpClient() { CloseableHttpClient httpClient = HttpClients.custom() .setConnectionManager(cm) .build(); /*CloseableHttpClient httpClient = HttpClients.createDefault();//如果不采用連接池就是這種方式獲取連接*/ return httpClient; } }
使用連接池代碼
private static CloseableHttpClient getHttpClient(){ HttpConnectionManager httpConnectionManager= (HttpConnectionManager) AppBeanUtil.getBean("httpConnectionManager"); return httpConnectionManager.getHttpClient(); }
獲取連接
public static String getFromUrl(String url, Map<String, String> params,String charset) throws Exception { if(StringUtil.isEmpty(charset)){ charset=defaultCharset; } CloseableHttpClient httpclient = getHttpClient(); URIBuilder uriBuilder = new URIBuilder(url); uriBuilder.setCharset(Charset.forName(charset)); if(params!=null){ Iterator<String> keyIt = params.keySet().iterator(); while (keyIt.hasNext()) { String key = keyIt.next(); String val = params.get(key); uriBuilder.setParameter(key, val); } } URI uri = uriBuilder.build(); HttpGet httpget = new HttpGet(uri); CloseableHttpResponse response = httpclient.execute(httpget); try { HttpEntity entity = response.getEntity(); if (entity != null) { String content=getContent(entity.getContent()); return content; } } finally { response.close(); } return null; }
訪問url 獲取內容
private static String getContent(InputStream instream) throws IOException { StringWriter writer = new StringWriter(); IOUtils.copy(instream, writer, defaultCharset); instream.close(); return writer.toString(); }
讀取數據,這里 instream 需要進行關閉。
測試連接池和不使用連接池的效率測試。
@Test public void postTest() throws Exception { long start=System.currentTimeMillis(); for (int i = 0; i < 200; i++) { String content=HttpClientPoolUtil.postFromUrl("https://www.cnblogs.com/kingszelda/p/8988505.html",new HashMap<>()); } System.err.println("---------------------------postTest---------------------------"); System.err.println(System.currentTimeMillis()-start); System.err.println("---------------------------postTest---------------------------"); start=System.currentTimeMillis(); for (int i = 0; i < 200; i++) { String content= HttpClientUtil.postFromUrl("https://www.cnblogs.com/kingszelda/p/8988505.html",new HashMap<>()); } System.err.println(System.currentTimeMillis()-start); }
測試結果
---------------------------postTest---------------------------
9791
---------------------------postTest---------------------------
39427
可以看到在使用連接池訪問兩百次花費時間為 9.7秒,不使用連接池為 39.4秒,時間相差為30秒。
通過計算從發送請求到接收請求約花48毫秒,每建立一次連接需要花費的時間為150毫秒,可見創建連接是需要非常消耗性能的。