httpclient高频请求
生活随笔
收集整理的這篇文章主要介紹了
httpclient高频请求
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
1、場景
有某個業務需要使用https請求內部服務。發現服務器下載寬帶占用達到30M/s。大大超出寬帶可承受的范圍。
2、分析
通過抓包發現TLS的證書占用很大,并且每次tcp都需要申請和獲取證書。
http每次請求都需要三次握手和四次揮手,如果是高頻請求的話,反復創建和銷毀是十分耗時的。https因為還需要申請證書更是耗時。
3、實現
package com.world.util;import org.apache.http.*; import org.apache.http.client.HttpRequestRetryHandler; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpRequestBase; import org.apache.http.client.protocol.HttpClientContext; import org.apache.http.config.Registry; import org.apache.http.config.RegistryBuilder; import org.apache.http.conn.ConnectTimeoutException; import org.apache.http.conn.routing.HttpRoute; 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 org.apache.http.message.BasicNameValuePair; import org.apache.http.protocol.HttpContext; import org.apache.http.util.EntityUtils; import org.slf4j.LoggerFactory;import javax.net.ssl.SSLException; import javax.net.ssl.SSLHandshakeException; import java.io.IOException; import java.io.InputStream; import java.io.InterruptedIOException; import java.io.UnsupportedEncodingException; import java.net.UnknownHostException; import java.util.*;/*** HttpClient工具類*/ public class HttpPoolKit {private static final org.slf4j.Logger log = LoggerFactory.getLogger(HttpPoolKit.class);private static int timeOut = 10 * 1000;private static int connectTimeOut = 10 * 1000;private static int socketTimeOut = 10 * 1000;private static int maxTotal = 100;private static int maxPerRoute = 10;private static int maxRoute = 10;private static CloseableHttpClient httpClient = null;private final static Object syncLock = new Object();static {InputStream stream = HttpPoolKit.class.getClassLoader().getResourceAsStream("main.properties");Properties props = new Properties();try {props.load(stream);timeOut = Integer.valueOf(props.getProperty("timeOut"));connectTimeOut = Integer.valueOf(props.getProperty("connectTimeOut"));socketTimeOut = Integer.valueOf(props.getProperty("socketTimeOut"));maxTotal = Integer.valueOf(props.getProperty("maxTotal"));maxPerRoute = Integer.valueOf(props.getProperty("maxPerRoute"));maxRoute = Integer.valueOf(props.getProperty("maxRoute"));} catch (Exception e) {log.error("load properties error", e);}}private static void config(HttpRequestBase httpRequestBase) {// 設置Header等// httpRequestBase.setHeader("User-Agent", "Mozilla/5.0");// httpRequestBase.setHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");// httpRequestBase.setHeader("Accept-Language","zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3");// "en-US,en;q=0.5");// httpRequestBase.setHeader("Accept-Charset","ISO-8859-1,utf-8,gbk,gb2312;q=0.7,*;q=0.7");// 配置請求的超時設置RequestConfig requestConfig = RequestConfig.custom().setConnectionRequestTimeout(timeOut) // 連接請求超時設置.setConnectTimeout(connectTimeOut) // 連接超時設置.setSocketTimeout(socketTimeOut) // socket通信超時設置.build();httpRequestBase.setConfig(requestConfig);}/*** 獲取HttpClient對象** @return*/public static CloseableHttpClient getHttpClient(String url) {// https://www.aaa.com/bbb/ccc http://www.aaa.com/bbb/ccc aaa.com/bbb/cccString hostname = url.split("/")[2];int port = 80;if (hostname.contains(":")) {String[] arr = hostname.split(":");hostname = arr[0];port = Integer.parseInt(arr[1]);}// 雙重校驗鎖if (httpClient == null) {synchronized (syncLock) {if (httpClient == null) {httpClient = createHttpClient(maxTotal, maxPerRoute, maxRoute, hostname, port);}}}return httpClient;}/*** 創建HttpClient對象** @param maxTotal* @param maxPerRoute* @param maxRoute* @param hostname* @param port* @return*/public static CloseableHttpClient createHttpClient(int maxTotal,int maxPerRoute,int maxRoute,String hostname,int port) {ConnectionSocketFactory plainsf = PlainConnectionSocketFactory.getSocketFactory();LayeredConnectionSocketFactory sslsf = SSLConnectionSocketFactory.getSocketFactory();Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create().register("http", plainsf).register("https", sslsf).build();PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(registry);// 將最大連接數增加cm.setMaxTotal(maxTotal);// 將每個路由基礎的連接增加cm.setDefaultMaxPerRoute(maxPerRoute);HttpHost httpHost = new HttpHost(hostname, port);// 將目標主機的最大連接數增加cm.setMaxPerRoute(new HttpRoute(httpHost), maxRoute);// 請求重試處理HttpRequestRetryHandler httpRequestRetryHandler = new HttpRequestRetryHandler() {@Overridepublic boolean retryRequest(IOException exception,int executionCount, HttpContext context) {if (executionCount >= 5) {// 如果已經重試了5次,就放棄return false;}if (exception instanceof NoHttpResponseException) {// 如果服務器丟掉了連接,那么就重試return true;}if (exception instanceof SSLHandshakeException) {// 不要重試SSL握手異常return false;}if (exception instanceof InterruptedIOException) {// 超時return false;}if (exception instanceof UnknownHostException) {// 目標服務器不可達return false;}if (exception instanceof ConnectTimeoutException) {// 連接被拒絕return false;}if (exception instanceof SSLException) {// SSL握手異常return false;}HttpClientContext clientContext = HttpClientContext.adapt(context);HttpRequest request = clientContext.getRequest();// 如果請求不是冪等的,就再次嘗試if (!(request instanceof HttpEntityEnclosingRequest)) {return true;}return false;}};CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(cm).setRetryHandler(httpRequestRetryHandler).build();return httpClient;}/*** @param httpost* @param params*/private static void setPostParams(HttpPost httpost,Map<String, Object> params) {List<NameValuePair> nvps = new ArrayList<>();Set<String> keySet = params.keySet();for (String key : keySet) {nvps.add(new BasicNameValuePair(key, params.get(key).toString()));}try {httpost.setEntity(new UrlEncodedFormEntity(nvps, "UTF-8"));} catch (UnsupportedEncodingException e) {log.error("error", e);}}/*** POST請求URL獲取內容** @param url* @return* @throws IOException*/public static String post(String url, Map<String, Object> params) throws IOException {HttpPost httppost = new HttpPost(url);config(httppost);setPostParams(httppost, params);CloseableHttpResponse response = null;try {response = getHttpClient(url).execute(httppost,HttpClientContext.create());HttpEntity entity = response.getEntity();String result = EntityUtils.toString(entity, "utf-8");EntityUtils.consume(entity);return result;} catch (Exception e) {log.error("error", e);throw e;} finally {try {if (response != null)response.close();} catch (IOException e) {log.error("error", e);}}}/*** GET請求URL獲取內容** @param url* @return*/public static String get(String url) {HttpGet httpget = new HttpGet(url);config(httpget);CloseableHttpResponse response = null;try {response = getHttpClient(url).execute(httpget, HttpClientContext.create());HttpEntity entity = response.getEntity();String result = EntityUtils.toString(entity, "utf-8");EntityUtils.consume(entity);return result;} catch (IOException e) {log.error("error", e);} finally {try {if (response != null) {response.close();}} catch (IOException e) {log.error("error", e);}}return null;}}測試代碼
package com.eblly.network;import com.eblly.util.HttpUtil; import org.junit.Test;import java.io.IOException; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicInteger;/*** 測試http請求效率** @author eblly* @since 2018/10/3*/ public class MultiHttpTs_2 {@Testpublic void httpTs() throws InterruptedException {request("測試4-1", 1000, 100);request("測試4-2", 10000, 100); // request("測試4-3", 100000, 100);//不使用http連接池//測試4-1 ==>耗時: 3697.0毫秒//測試4-2 ==>耗時: 30483.0毫秒//使用http連接池//測試4-1 ==>耗時: 2023.0毫秒//測試4-2 ==>耗時: 10909.0毫秒}/*** @param msg* @param requestSize* @param threadSize* @throws InterruptedException*/private void request(String msg, int requestSize, int threadSize) throws InterruptedException {CountDownLatch countDownLatch = null;AtomicInteger atomicInteger = null;countDownLatch = new CountDownLatch(requestSize);atomicInteger = new AtomicInteger(requestSize);long startTime = System.currentTimeMillis();ExecutorService executorService = Executors.newFixedThreadPool(threadSize);//使用線程池for (int i = 0; i < threadSize; i++) {executorService.execute(new DownRunnable(countDownLatch, atomicInteger));}countDownLatch.await();long endTime = System.currentTimeMillis();double time = (endTime - startTime);System.out.println(msg + " ==>耗時: " + time + "毫秒");}/*** 下載線程*/private class DownRunnable implements Runnable {private CountDownLatch countDownLatch;private AtomicInteger atomicInteger;public static final String host = "https://www.baidu.com";public DownRunnable(CountDownLatch countDownLatch, AtomicInteger atomicInteger) {super();this.countDownLatch = countDownLatch;this.atomicInteger = atomicInteger;}@Overridepublic void run() {try {while (atomicInteger.decrementAndGet() >= 0) {try { // String reposne = HttpUtil.get(host, "");String reposne = HttpClientUtil.get(host);} catch (Exception e) {e.printStackTrace();}countDownLatch.countDown();}} catch (Exception e) {e.printStackTrace();}}}}測試結果
//不使用http連接池//測試4-1 ==>耗時: 3697.0毫秒//測試4-2 ==>耗時: 30483.0毫秒//使用http連接池//測試4-1 ==>耗時: 2023.0毫秒//測試4-2 ==>耗時: 10909.0毫秒3.1、對比
未使用http連接池,客戶端重復向服務端請求證書。
使用http連接池,不再重復申請證書。
參考:
- http://www.cnblogs.com/bethunebtj/p/8493379.html
- https://yq.aliyun.com/articles/294
轉載于:https://www.cnblogs.com/eblly/p/10392998.html
總結
以上是生活随笔為你收集整理的httpclient高频请求的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 开发进度六
- 下一篇: 基于ATT和CK™框架的开放式方法评估网