HttpClient客戶端網路程式設計——高可用、高併發

複姓江山發表於2021-02-09

  本文是HttpClient的學習部落格,RestTemplate是基於HttpClient的封裝,feign可基於HttpClient進行網路通訊。

  那麼作為較底層的客戶端網路程式設計框架,該怎麼配置使其能高可用,高併發,可支援Https協議呢?通讀本文也許你會有答案或者啟發。

  本文是Maven專案,基於Spring,在本Demo中使用了更方便的SpringBoot。

  以後隨著理解HttpClient更深入的時候會不定期更新本部落格,也歡迎感興趣的博友交流和討論。

一、本文目錄

  1. 程式碼實現

  2. 測試驗證

  3. 後記

二、程式碼實現

1. 專案依賴

HttpClient客戶端網路程式設計——高可用、高併發
 1 <dependency>
 2             <groupId>org.springframework.boot</groupId>
 3             <artifactId>spring-boot-starter</artifactId>
 4         </dependency>
 5 
 6         <!--httpclient-->
 7         <dependency>
 8             <groupId>org.apache.httpcomponents</groupId>
 9             <artifactId>httpclient</artifactId>
10             <version>4.5.12</version>
11         </dependency>
12 
13         <!--alibaba JSON-->
14         <dependency>
15             <groupId>com.alibaba</groupId>
16             <artifactId>fastjson</artifactId>
17             <version>1.2.70</version>
18         </dependency>
19 
20         <dependency>
21             <groupId>org.springframework.boot</groupId>
22             <artifactId>spring-boot-starter-test</artifactId>
23             <scope>test</scope>
24         </dependency>
pom.xml

2. 專案結構

  

3. 專案配置

HttpClient客戶端網路程式設計——高可用、高併發
 1 #HttpClient配置
 2 httpClient:
 3   #重試次數
 4   retryCount: 3
 5   #重啟開關
 6   requestSentRetryEnabled: true
 7   #HttpClient連線池配置
 8   pool:
 9     #總連線數
10     maxTotal: 200
11     #每個路由預設連線數,某一個/每服務每次能並行接收的請求數量
12     defaultMaxPerRoute: 50
13     #Validate connections after 15 sec of inactivity  15000
14     validateAfterInactivity: 1000
15     #idle超時時間
16     idleTimeOut: 3
17     socketCfg:
18       #是否立即傳送資料,設定為true會關閉Socket緩衝,預設為false
19       tcpNoDelay: true
20       #是否可以在一個程式關閉Socket後,即使它還沒有釋放埠,其它程式還可以立即重用埠
21       soReuseAddress: true
22       #接受資料的等待超時時間,單位ms
23       soTimeOut: 500
24       #關閉Socket時,要麼傳送完所有資料,要麼等待60s後,就關閉連線,此時socket.close()是阻塞的
25       soLinger: 60
26       #開啟監視TCP連線是否有效
27       soKeepAlive: true
application.yml

4. HttpClient客戶端配置類

  通過配置連線池管理物件PoolingHttpClientConnectionManager,設定兩個重要引數maxTotal和defaultMaxPerRoute,及其其它引數。本文引數配置參考官方文件httpcomponents-client-4.5.x,文件上面的引數更多更齊全,包括HttpConnectionFactory、DnsResolver、ConnectionConfig、RequestConfig、RequestConfig、HttpClientContext、設定代理。這些引數本文沒有配置,使用HttpClient的預設配置,感興趣想繼續深入研究的可以去學習瞭解。BackoffManager可以在連線池處於閒暇時進行收縮,不過網路上資料較少,目前還沒研究出怎麼使用和配置。

HttpClient客戶端網路程式設計——高可用、高併發
  1 package com.example.httpclientdemo.common.config.http.client;
  2 
  3 import org.apache.http.config.Registry;
  4 import org.apache.http.config.RegistryBuilder;
  5 import org.apache.http.config.SocketConfig;
  6 import org.apache.http.conn.socket.ConnectionSocketFactory;
  7 import org.apache.http.conn.socket.PlainConnectionSocketFactory;
  8 import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
  9 import org.apache.http.impl.client.*;
 10 import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
 11 import org.apache.http.ssl.SSLContexts;
 12 import org.springframework.beans.factory.annotation.Value;
 13 import org.springframework.context.annotation.Bean;
 14 import org.springframework.context.annotation.Configuration;
 15 
 16 import java.util.concurrent.TimeUnit;
 17 
 18 /**
 19  * HttpClient客戶端配置類
 20  *
 21  * @author 複姓江山
 22  * @date 2021/02/08
 23  */
 24 @Configuration
 25 public class HttpClientConfig {
 26 
 27     /**
 28      * closeableHttpClient連線物件,支援HTTPS使用SSL套接層
 29      *
 30      * @param httpClientPoolManager   HttpClient連線池管理物件
 31      * @param retryCount              重試次數
 32      * @param requestSentRetryEnabled 重啟開關
 33      * @return closeableHttpClient連線物件
 34      */
 35     @Bean
 36     public CloseableHttpClient closeableHttpClient(final PoolingHttpClientConnectionManager httpClientPoolManager,
 37                                                    @Value("${httpClient.retryCount}") final int retryCount,
 38                                                    @Value("${httpClient.requestSentRetryEnabled}") final
 39                                                    boolean requestSentRetryEnabled) {
 40 
 41         return HttpClients.custom()
 42                 .setDefaultCookieStore(new BasicCookieStore())
 43                 .setDefaultCredentialsProvider(new BasicCredentialsProvider())
 44                 .setConnectionManager(httpClientPoolManager)
 45                 .setRetryHandler(new DefaultHttpRequestRetryHandler(retryCount, requestSentRetryEnabled))
 46                 .build();
 47     }
 48 
 49     /**
 50      * 預設socket configuration
 51      *
 52      * @param tcpNoDelay     是否立即傳送資料,設定為true會關閉Socket緩衝,預設為false
 53      * @param soReuseAddress 是否可以在一個程式關閉Socket後,即使它還沒有釋放埠,其它程式還可以立即重用埠
 54      * @param soTimeOut      接受資料的等待超時時間,單位ms
 55      * @param soLinger       關閉Socket時,要麼傳送完所有資料,要麼等待60s後,就關閉連線,此時socket.close()是阻塞的
 56      * @param soKeepAlive    開啟監視TCP連線是否有效
 57      * @return 預設socket configuration
 58      */
 59     @Bean
 60     public SocketConfig defaultSocketConfig(@Value("${httpClient.pool.socketCfg.tcpNoDelay}") final boolean tcpNoDelay,
 61                                             @Value("${httpClient.pool.socketCfg.soReuseAddress}") final boolean soReuseAddress,
 62                                             @Value("${httpClient.pool.socketCfg.soTimeOut}") final int soTimeOut,
 63                                             @Value("${httpClient.pool.socketCfg.soLinger}") final int soLinger,
 64                                             @Value("${httpClient.pool.socketCfg.soKeepAlive}") final boolean soKeepAlive) {
 65         return SocketConfig.custom()
 66                 .setTcpNoDelay(tcpNoDelay)
 67                 .setSoReuseAddress(soReuseAddress)
 68                 .setSoTimeout(soTimeOut)
 69                 .setSoLinger(soLinger)
 70                 .setSoKeepAlive(soKeepAlive).build();
 71     }
 72 
 73     /**
 74      * HttpClient連線池管理物件
 75      *
 76      * @param maxTotal                總連線數
 77      * @param defaultMaxPerRoute      每個路由預設連線數,某一個/每服務每次能並行接收的請求數量
 78      * @param validateAfterInactivity 一次連線保留時長,單位s
 79      * @param idleTimeOut             idle超時時間
 80      * @param defaultSocketConfig     預設socket configuration
 81      * @return HttpClient連線池管理物件
 82      */
 83     @Bean
 84     public PoolingHttpClientConnectionManager httpClientPoolManager(
 85             @Value("${httpClient.pool.maxTotal}") final int maxTotal,
 86             @Value("${httpClient.pool.defaultMaxPerRoute}") final int defaultMaxPerRoute,
 87             @Value("${httpClient.pool.validateAfterInactivity}") final int validateAfterInactivity,
 88             @Value("${httpClient.pool.validateAfterInactivity}") final long idleTimeOut,
 89             final SocketConfig defaultSocketConfig) {
 90         Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
 91                 .register("http", PlainConnectionSocketFactory.INSTANCE)
 92                 .register("https", new SSLConnectionSocketFactory(SSLContexts.createSystemDefault()))
 93                 .build();
 94         PoolingHttpClientConnectionManager poolManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
 95         poolManager.setMaxTotal(maxTotal);
 96         poolManager.setDefaultMaxPerRoute(defaultMaxPerRoute);
 97         poolManager.setValidateAfterInactivity(validateAfterInactivity);
 98         poolManager.closeIdleConnections(idleTimeOut, TimeUnit.SECONDS);
 99         poolManager.setDefaultSocketConfig(defaultSocketConfig);
100         return poolManager;
101     }
102 
103 }
HttpClientConfig.java

5. HttpClient工具類

HttpClient客戶端網路程式設計——高可用、高併發
  1 package com.example.httpclientdemo.common.utils.http.client;
  2 
  3 import org.apache.http.HttpEntity;
  4 import org.apache.http.client.methods.*;
  5 import org.apache.http.impl.client.CloseableHttpClient;
  6 import org.apache.http.impl.execchain.RequestAbortedException;
  7 
  8 import java.io.*;
  9 import java.util.stream.Collectors;
 10 
 11 /**
 12  * HttpClient工具類
 13  *
 14  * @author 複姓江山
 15  * @date 2021/02/08
 16  */
 17 public final class HttpClientUtils {
 18 
 19     /**
 20      * header的Content-Type鍵
 21      */
 22     public final static String HEADER_CONTENT_TYPE = "Content-Type";
 23 
 24     /**
 25      * R3C預設Content_Type
 26      */
 27     public final static String R3C_DEFAULT_CONTENT_TYPE = "application/vnd.api+json";
 28 
 29     /**
 30      * Authorization
 31      */
 32     public final static String HEADER_AUTHORIZATION = "Authorization";
 33 
 34     private HttpClientUtils() {
 35 
 36     }
 37 
 38     /**
 39      * httpClient的響應實體
 40      *
 41      * @param httpClient httpClient物件
 42      * @param request    請求物件
 43      * @return 響應實體
 44      * @throws IOException IO異常
 45      */
 46     private static CloseableHttpResponse httpResponse(CloseableHttpClient httpClient,
 47                                                       HttpUriRequest request) throws IOException {
 48         return httpClient.execute(request);
 49     }
 50 
 51     /**
 52      * get請求
 53      *
 54      * @param httpClient httpClient物件
 55      * @param request    請求物件
 56      * @return 響應實體
 57      * @throws IOException IO異常
 58      */
 59     public static CloseableHttpResponse get(CloseableHttpClient httpClient, HttpUriRequest request) throws IOException {
 60         assert HttpGet.METHOD_NAME.equals(request.getMethod());
 61         return HttpClientUtils.httpResponse(httpClient, request);
 62     }
 63 
 64     /**
 65      * post請求
 66      *
 67      * @param httpClient httpClient物件
 68      * @param request    請求物件
 69      * @return 響應實體
 70      * @throws IOException IO異常
 71      */
 72     public static CloseableHttpResponse post(CloseableHttpClient httpClient, HttpUriRequest request) throws IOException {
 73         assert HttpPost.METHOD_NAME.equals(request.getMethod());
 74         return HttpClientUtils.httpResponse(httpClient, request);
 75     }
 76 
 77     /**
 78      * post或patch請求
 79      *
 80      * @param httpClient httpClient物件
 81      * @param request    請求物件
 82      * @return 響應實體
 83      * @throws IOException IO異常
 84      */
 85     public static CloseableHttpResponse postOrPatch(CloseableHttpClient httpClient, HttpUriRequest request) throws IOException {
 86         if (request instanceof HttpPost) {
 87             assert HttpPost.METHOD_NAME.equals(request.getMethod());
 88         } else if (request instanceof HttpPatch) {
 89             assert HttpPatch.METHOD_NAME.equals(request.getMethod());
 90         } else {
 91             throw new RequestAbortedException("Not post or patch.");
 92         }
 93         return HttpClientUtils.httpResponse(httpClient, request);
 94     }
 95 
 96     /**
 97      * patch請求
 98      *
 99      * @param httpClient httpClient物件
100      * @param request    請求物件
101      * @return 響應實體
102      * @throws IOException IO異常
103      */
104     public static CloseableHttpResponse patch(CloseableHttpClient httpClient, HttpUriRequest request) throws IOException {
105         assert HttpPatch.METHOD_NAME.equals(request.getMethod());
106         return HttpClientUtils.httpResponse(httpClient, request);
107     }
108 
109     /**
110      * httpClient的HttpEntity
111      *
112      * @param response 響應實體
113      * @return HttpEntity
114      */
115     public static HttpEntity httpEntity(final CloseableHttpResponse response) {
116         return response.getEntity();
117     }
118 
119 
120     /**
121      * 返回狀態行程式碼
122      *
123      * @param response 響應實體
124      * @return 狀態行程式碼
125      */
126     public static int getStatusCode(final CloseableHttpResponse response) {
127         return response.getStatusLine().getStatusCode();
128     }
129 
130     /**
131      * 獲取ContentType
132      *
133      * @param httpEntity HttpEntity
134      * @return ContentType
135      */
136     public static String getContentType(final HttpEntity httpEntity) {
137         return httpEntity.getContentType().getValue();
138     }
139 
140     /**
141      * 獲取ContentEncoding
142      *
143      * @param httpEntity HttpEntity
144      * @return ContentEncoding
145      */
146     public static String getContentEncoding(final HttpEntity httpEntity) {
147         return httpEntity.getContentEncoding().getValue();
148     }
149 
150     /**
151      * 獲取響應體物件InputStream
152      *
153      * @param httpEntity HttpEntity
154      * @return 獲取響應體物件InputStream
155      * @throws IOException IO異常
156      */
157     public static InputStream getContent(final HttpEntity httpEntity) throws IOException {
158         return httpEntity.getContent();
159     }
160 
161     /**
162      * 獲取響應體物件的字串
163      *
164      * @param inputStream InputStream
165      * @return 獲取響應體物件InputStream
166      * @throws IOException IO異常
167      */
168     public static String getContentString(final InputStream inputStream) throws IOException {
169         return new BufferedReader(new InputStreamReader(inputStream))
170                 .lines().parallel().collect(Collectors.joining(System.lineSeparator()));
171     }
172 
173     /**
174      * 獲取響應體物件的字串
175      *
176      * @param httpEntity HttpEntity
177      * @return 獲取響應體物件InputStream
178      * @throws IOException IO異常
179      */
180     public static String getContentString(final HttpEntity httpEntity) throws IOException {
181         return new BufferedReader(new InputStreamReader(HttpClientUtils.getContent(httpEntity)))
182                 .lines().parallel().collect(Collectors.joining(System.lineSeparator()));
183     }
184 
185     /**
186      * 字串轉InputStream
187      *
188      * @param str 字串
189      * @return inputStream
190      */
191     public static InputStream stringTransferToInputStream(String str) {
192         return new ByteArrayInputStream(str.getBytes());
193     }
194 
195     /**
196      * 設定預設的Content-Type
197      *
198      * @param request http請求物件
199      */
200     public static void setDefaultContentType(HttpUriRequest request) {
201         request.setHeader(HttpClientUtils.HEADER_CONTENT_TYPE, HttpClientUtils.R3C_DEFAULT_CONTENT_TYPE);
202     }
203 
204     /**
205      * 設定Authorization
206      *
207      * @param request http請求物件
208      * @param token   token
209      */
210     public static void setAuthorization(HttpUriRequest request, String token) {
211         request.setHeader(HttpClientUtils.HEADER_AUTHORIZATION, token);
212     }
213 
214     /**
215      * 設定預設請求頭
216      *
217      * @param request http請求物件
218      * @param token   token
219      */
220     public static void setDefaultHeader(HttpUriRequest request, String token) {
221         HttpClientUtils.setDefaultContentType(request);
222         HttpClientUtils.setAuthorization(request, token);
223     }
224 
225     /**
226      * 關閉連線
227      *
228      * @param response    response響應物件
229      * @param inputStream inputStream
230      */
231     public static void close(CloseableHttpResponse response, InputStream inputStream) throws IOException {
232         if (inputStream != null) {
233             inputStream.close();
234         }
235         if (response != null) {
236             response.close();
237         }
238     }
239 
240 
241 }
HttpClientUtils.java

6. HttpClient業務介面及其實現

HttpClient客戶端網路程式設計——高可用、高併發
 1 package com.example.httpclientdemo.biz.service;
 2 
 3 /**
 4  * HttpClient業務介面類
 5  *
 6  * @author 複姓江山
 7  * @date 2021/02/08
 8  */
 9 public interface HttpClientService {
10     /**
11      * get請求
12      *
13      * @param url 請求地址
14      */
15     void requestGet(String url);
16 
17     /**
18      * post請求
19      *
20      * @param url 請求地址
21      * @param obj 請求物件
22      */
23     void requestPost(String url, Object obj);
24 }
HttpClientService.java

 

HttpClient客戶端網路程式設計——高可用、高併發
 1 package com.example.httpclientdemo.biz.service.impl;
 2 
 3 import com.example.httpclientdemo.biz.provider.HttpClientProvider;
 4 import com.example.httpclientdemo.biz.service.HttpClientService;
 5 import org.springframework.beans.factory.annotation.Autowired;
 6 import org.springframework.stereotype.Service;
 7 
 8 /**
 9  * HttpClient業務介面實現類
10  *
11  * @author 複姓江山
12  * @date 2021/02/08
13  */
14 @Service
15 public class HttpClientServiceImpl implements HttpClientService {
16 
17     @Autowired
18     private HttpClientProvider httpClientProvider;
19 
20 
21     @Override
22     public void requestGet(String url) {
23         httpClientProvider.requestGet(url);
24     }
25 
26     @Override
27     public void requestPost(String url, Object obj) {
28         httpClientProvider.requestPost(url, obj);
29     }
30 }
HttpClientServiceImpl.java

7. HttpClient業務支撐介面及其實現

  為什麼會加個業務支撐介面呢?常見的分層結構或分層架構(控制層、業務層和資料訪問層——分層結構,或表示層(UI)、業務邏輯層(BLL)和資料訪問層——分層架構),在此基礎上增加provder是因為多層封裝與隔離,HttpClient是作為客戶端請求其它系統服務,請求引數與相應引數及異常處理應與本專案進行一定隔離,如果把HttpClient客戶端深度耦合到業務層中,到對方伺服器變動時,就會影響本系統的核心業務邏輯。而增加了封裝與隔離後,影響的只是provider層,系統本身的核心業務邏輯不會受到深層次的影響。在這我就稍微發散一下思維,比如定義一個MQ的介面,在使用的是Kafka,RabitMQ,ActiveMQ或者RocketMQ,不管怎麼變更MQ,系統業務程式碼依賴的是MQ的介面,絲毫不會受到影響。所以程式設計原則裡有一條:面向介面程式設計,面向抽象程式設計。

HttpClient客戶端網路程式設計——高可用、高併發
 1 package com.example.httpclientdemo.biz.provider;
 2 
 3 /**
 4  * HttpClient業務支撐介面類
 5  *
 6  * @author 複姓江山
 7  * @date 2021/02/08
 8  */
 9 public interface HttpClientProvider {
10 
11     /**
12      * get請求
13      *
14      * @param url 請求地址
15      */
16     void requestGet(String url);
17 
18     /**
19      * post請求
20      *
21      * @param url 請求地址
22      * @param obj 請求引數
23      */
24     void requestPost(String url, Object obj);
25 }
HttpClientProvider.java

 

HttpClient客戶端網路程式設計——高可用、高併發
 1 package com.example.httpclientdemo.biz.provider.impl;
 2 
 3 import com.alibaba.fastjson.JSON;
 4 import com.example.httpclientdemo.biz.provider.HttpClientProvider;
 5 import com.example.httpclientdemo.common.utils.http.client.HttpClientUtils;
 6 import org.apache.http.HttpEntity;
 7 import org.apache.http.client.methods.CloseableHttpResponse;
 8 import org.apache.http.client.methods.HttpGet;
 9 import org.apache.http.client.methods.HttpPost;
10 import org.apache.http.entity.StringEntity;
11 import org.apache.http.impl.client.CloseableHttpClient;
12 import org.slf4j.Logger;
13 import org.slf4j.LoggerFactory;
14 import org.springframework.stereotype.Service;
15 
16 import javax.annotation.Resource;
17 import java.io.IOException;
18 import java.io.InputStream;
19 import java.nio.charset.StandardCharsets;
20 
21 /**
22  * HttpClient業務支撐介面實現類
23  *
24  * @author 複姓江山
25  * @date 2021/02/08
26  */
27 @Service
28 public class HttpClientProviderImpl implements HttpClientProvider {
29     private final Logger logger = LoggerFactory.getLogger(this.getClass());
30 
31     @Resource(name = "closeableHttpClient")
32     private CloseableHttpClient httpClient;
33 
34 
35     @Override
36     public void requestGet(String url) {
37         HttpGet httpGet = new HttpGet(url);
38         CloseableHttpResponse response = null;
39         InputStream inputStream = null;
40 
41         try {
42             response = HttpClientUtils.get(httpClient, httpGet);
43             HttpEntity httpEntity = response.getEntity();
44             inputStream = HttpClientUtils.getContent(httpEntity);
45             String respString = HttpClientUtils.getContentString(inputStream);
46             logger.debug("respString: {}", respString);
47         } catch (Exception e) {
48             e.fillInStackTrace();
49         } finally {
50             try {
51                 HttpClientUtils.close(response, inputStream);
52             } catch (IOException ioException) {
53                 ioException.printStackTrace();
54             }
55         }
56     }
57 
58     @Override
59     public void requestPost(String url, Object obj) {
60         HttpPost httpPost = new HttpPost(url);
61         String jsonString = JSON.toJSONString(obj);
62         httpPost.setEntity(new StringEntity(jsonString, StandardCharsets.UTF_8));
63         CloseableHttpResponse response = null;
64         InputStream inputStream = null;
65         try {
66             response = HttpClientUtils.post(httpClient, httpPost);
67             HttpEntity httpEntity = response.getEntity();
68             inputStream = HttpClientUtils.getContent(httpEntity);
69             String respString = HttpClientUtils.getContentString(inputStream);
70             logger.debug("respString: {}", respString);
71         } catch (Exception e) {
72             e.fillInStackTrace();
73         } finally {
74             try {
75                 HttpClientUtils.close(response, inputStream);
76             } catch (IOException ioException) {
77                 ioException.printStackTrace();
78             }
79         }
80     }
81 }
HttpClientProviderImpl.java

  8. HttpClient業務介面測試

  通過執行單元測試類來測試介面,下文有詳細的測試資料。

HttpClient客戶端網路程式設計——高可用、高併發
 1 package com.example.httpclientdemo.biz.service;
 2 
 3 import org.junit.jupiter.api.Test;
 4 import org.slf4j.Logger;
 5 import org.slf4j.LoggerFactory;
 6 import org.springframework.beans.factory.annotation.Autowired;
 7 import org.springframework.boot.test.context.SpringBootTest;
 8 
 9 import java.util.concurrent.ExecutorService;
10 import java.util.concurrent.Executors;
11 import java.util.concurrent.atomic.AtomicInteger;
12 
13 import static org.junit.jupiter.api.Assertions.*;
14 
15 @SpringBootTest
16 class HttpClientServiceTest {
17     private final static Logger logger = LoggerFactory.getLogger(HttpClientServiceTest.class);
18 
19     @Autowired
20     private HttpClientService httpClientService;
21 
22     private static final String URL_GET_PATH = "https://www.baidu.com";
23 
24     private final AtomicInteger count = new AtomicInteger();
25 
26     /**
27      * workStealingPool
28      */
29     public final static ExecutorService workStealingPool = Executors.newWorkStealingPool(1<<6);
30 
31     @Test
32     void requestGet() throws InterruptedException {
33         final long startMils = System.currentTimeMillis();
34         final long statNanos = System.nanoTime();
35         for (int i = 0; i < 1000; i++) {
36             workStealingPool.execute(() -> {
37                 httpClientService.requestGet(URL_GET_PATH);
38                 logger.warn("requestGet, times: {}, betMils: {},betNanos: {}", count.getAndIncrement(),
39                         (System.currentTimeMillis() - startMils), (System.nanoTime() - statNanos));
40             });
41 
42         }
43         Thread.sleep(20000);
44     }
45 
46     @Test
47     void requestPost() {
48     }
49 }
HttpClientServiceTest.java

 

三、測試驗證

  本文測試方式可能不是那麼專業與嚴謹,可是並不妨礙我們通過測試資料看出些原理,總結出些規律,也許有些片面,但可看出些趨勢。

1. 執行日誌

HttpClient客戶端網路程式設計——高可用、高併發
 1 main 2021-02-09 14:09:34,963 DEBUG (PoolingHttpClientConnectionManager.java:267)- Connection request: [route: {s}->https://www.baidu.com:443][total available: 0; route allocated: 0 of 50; total allocated: 0 of 200]
 2 main 2021-02-09 14:09:34,978 DEBUG (PoolingHttpClientConnectionManager.java:312)- Connection leased: [id: 0][route: {s}->https://www.baidu.com:443][total available: 0; route allocated: 1 of 50; total allocated: 1 of 200]
 3 main 2021-02-09 14:09:34,980 DEBUG (MainClientExec.java:234)- Opening connection {s}->https://www.baidu.com:443
 4 main 2021-02-09 14:09:34,994 DEBUG (DefaultHttpClientConnectionOperator.java:139)- Connecting to www.baidu.com/14.215.177.38:443
 5 main 2021-02-09 14:09:34,994 DEBUG (SSLConnectionSocketFactory.java:366)- Connecting socket to www.baidu.com/14.215.177.38:443 with timeout 0
 6 main 2021-02-09 14:09:35,101 DEBUG (SSLConnectionSocketFactory.java:430)- Enabled protocols: [TLSv1, TLSv1.1, TLSv1.2]
 7 main 2021-02-09 14:09:35,101 DEBUG (SSLConnectionSocketFactory.java:431)- Enabled cipher suites:[TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, TLS_RSA_WITH_AES_256_CBC_SHA256, TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384, TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384, TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, TLS_DHE_DSS_WITH_AES_256_CBC_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, TLS_RSA_WITH_AES_256_CBC_SHA, TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, TLS_DHE_RSA_WITH_AES_256_CBC_SHA, TLS_DHE_DSS_WITH_AES_256_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, TLS_RSA_WITH_AES_128_CBC_SHA256, TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, TLS_DHE_DSS_WITH_AES_128_CBC_SHA256, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_DSS_WITH_AES_128_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, TLS_RSA_WITH_AES_256_GCM_SHA384, TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384, TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, TLS_DHE_DSS_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256, TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, TLS_DHE_DSS_WITH_AES_128_GCM_SHA256, TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
 8 main 2021-02-09 14:09:35,102 DEBUG (SSLConnectionSocketFactory.java:435)- Starting handshake
 9 main 2021-02-09 14:09:35,250 DEBUG (SSLConnectionSocketFactory.java:465)- Secure session established
10 main 2021-02-09 14:09:35,250 DEBUG (SSLConnectionSocketFactory.java:466)-  negotiated protocol: TLSv1.2
11 main 2021-02-09 14:09:35,250 DEBUG (SSLConnectionSocketFactory.java:467)-  negotiated cipher suite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
12 main 2021-02-09 14:09:35,250 DEBUG (SSLConnectionSocketFactory.java:475)-  peer principal: CN=baidu.com, O="Beijing Baidu Netcom Science Technology Co., Ltd", OU=service operation department, L=beijing, ST=beijing, C=CN
13 main 2021-02-09 14:09:35,251 DEBUG (SSLConnectionSocketFactory.java:484)-  peer alternative names: [baidu.com, baifubao.com, www.baidu.cn, www.baidu.com.cn, mct.y.nuomi.com, apollo.auto, dwz.cn, *.baidu.com, *.baifubao.com, *.baidustatic.com, *.bdstatic.com, *.bdimg.com, *.hao123.com, *.nuomi.com, *.chuanke.com, *.trustgo.com, *.bce.baidu.com, *.eyun.baidu.com, *.map.baidu.com, *.mbd.baidu.com, *.fanyi.baidu.com, *.baidubce.com, *.mipcdn.com, *.news.baidu.com, *.baidupcs.com, *.aipage.com, *.aipage.cn, *.bcehost.com, *.safe.baidu.com, *.im.baidu.com, *.baiducontent.com, *.dlnel.com, *.dlnel.org, *.dueros.baidu.com, *.su.baidu.com, *.91.com, *.hao123.baidu.com, *.apollo.auto, *.xueshu.baidu.com, *.bj.baidubce.com, *.gz.baidubce.com, *.smartapps.cn, *.bdtjrcv.com, *.hao222.com, *.haokan.com, *.pae.baidu.com, *.vd.bdstatic.com, click.hm.baidu.com, log.hm.baidu.com, cm.pos.baidu.com, wn.pos.baidu.com, update.pan.baidu.com]
14 main 2021-02-09 14:09:35,251 DEBUG (SSLConnectionSocketFactory.java:488)-  issuer principal: CN=GlobalSign Organization Validation CA - SHA256 - G2, O=GlobalSign nv-sa, C=BE
15 main 2021-02-09 14:09:35,254 DEBUG (DefaultHttpClientConnectionOperator.java:146)- Connection established 192.168.100.24:63820<->14.215.177.38:443
16 main 2021-02-09 14:09:35,255 DEBUG (MainClientExec.java:255)- Executing request GET / HTTP/1.1
17 main 2021-02-09 14:09:35,255 DEBUG (MainClientExec.java:260)- Target auth state: UNCHALLENGED
18 main 2021-02-09 14:09:35,255 DEBUG (MainClientExec.java:266)- Proxy auth state: UNCHALLENGED
19 main 2021-02-09 14:09:35,257 DEBUG (LoggingManagedHttpClientConnection.java:133)- http-outgoing-0 >> GET / HTTP/1.1
20 main 2021-02-09 14:09:35,257 DEBUG (LoggingManagedHttpClientConnection.java:136)- http-outgoing-0 >> Host: www.baidu.com
21 main 2021-02-09 14:09:35,257 DEBUG (LoggingManagedHttpClientConnection.java:136)- http-outgoing-0 >> Connection: Keep-Alive
22 main 2021-02-09 14:09:35,257 DEBUG (LoggingManagedHttpClientConnection.java:136)- http-outgoing-0 >> User-Agent: Apache-HttpClient/4.5.12 (Java/1.8.0_161)
23 main 2021-02-09 14:09:35,257 DEBUG (LoggingManagedHttpClientConnection.java:136)- http-outgoing-0 >> Accept-Encoding: gzip,deflate
24 main 2021-02-09 14:09:35,258 DEBUG (Wire.java:73)- http-outgoing-0 >> "GET / HTTP/1.1[\r][\n]"
25 main 2021-02-09 14:09:35,258 DEBUG (Wire.java:73)- http-outgoing-0 >> "Host: www.baidu.com[\r][\n]"
26 main 2021-02-09 14:09:35,258 DEBUG (Wire.java:73)- http-outgoing-0 >> "Connection: Keep-Alive[\r][\n]"
27 main 2021-02-09 14:09:35,258 DEBUG (Wire.java:73)- http-outgoing-0 >> "User-Agent: Apache-HttpClient/4.5.12 (Java/1.8.0_161)[\r][\n]"
28 main 2021-02-09 14:09:35,258 DEBUG (Wire.java:73)- http-outgoing-0 >> "Accept-Encoding: gzip,deflate[\r][\n]"
29 main 2021-02-09 14:09:35,258 DEBUG (Wire.java:73)- http-outgoing-0 >> "[\r][\n]"
30 main 2021-02-09 14:09:35,300 DEBUG (Wire.java:73)- http-outgoing-0 << "HTTP/1.1 200 OK[\r][\n]"
31 main 2021-02-09 14:09:35,301 DEBUG (Wire.java:73)- http-outgoing-0 << "Content-Encoding: gzip[\r][\n]"
32 main 2021-02-09 14:09:35,301 DEBUG (Wire.java:73)- http-outgoing-0 << "Content-Length: 1145[\r][\n]"
33 main 2021-02-09 14:09:35,301 DEBUG (Wire.java:73)- http-outgoing-0 << "Content-Type: text/html[\r][\n]"
34 main 2021-02-09 14:09:35,301 DEBUG (Wire.java:73)- http-outgoing-0 << "Server: bfe[\r][\n]"
35 main 2021-02-09 14:09:35,301 DEBUG (Wire.java:73)- http-outgoing-0 << "Date: Tue, 09 Feb 2021 06:09:34 GMT[\r][\n]"
36 main 2021-02-09 14:09:35,302 DEBUG (Wire.java:73)- http-outgoing-0 << "[\r][\n]"
37 main 2021-02-09 14:09:35,302 DEBUG (Wire.java:73)- http-outgoing-0 << "[0x1f][0x8b][0x8][0x0][0x0][0x0][0x0][0x0][0x0][0xff][0x94]V[[0x8f][0xd4][0xb6][0x17]G[0xe2];[0x98][0xfc][0xb5][0xbb] 4[0xe3][0xb9][0x8][0xc1]7[0x9][0xda]nABH[0x5][0x15]V*O#[0xc7]v[0x12][0xb3][0x89]mlg[0xc2][0xf0][0xd4][0x95]J[0xd5][0xaa][0xa5][0xb4][0xa2][0x17]Q*[0xb5][0xaa]Z[0xb6][0xf][0x95][0xa0]*R[0xd1]R[0xe0][0xcb]L[0xd8][0xdd][0xa7]~[0x85][0xca]If[0xe7][\n]"
38 main 2021-02-09 14:09:35,302 DEBUG (Wire.java:73)- http-outgoing-0 << "[0xa8][0xf3][0x12][0xfb][0xf8][0x9c][0xdf][0xf9][0x9d][0x9b]=[0xee][0xb1]w/m\[0xbd]v[0xf9][0x1c][0x88]M[0x9a][0xf8]G[0x8f][0xb8][0xc7][0x1a][0x8d]+W[0xd7][0xaf]n^[0x1][0x97].6[0x1a][0xbe][[0xca][0x81][0x1b]SD|7[0xa5][0x6][0x81][0xd8][0x18][0xd9][0xa0]72[0xd6][0xf7][0xb0][0xe0][0x86]r[0xd3]0[0x3]IA[0xbd][0xf1][0xc][0xbd]i[0xa0]5[[0xc3]1R[0x9a][0x1a]/3a[0xe3][0xcc][0xbc][0xf5][0x7][0x8d][0xcd][0xf5][0xc6][0x86]H%2,H[0xc6][0x0][0x17][0xce]y[0xe7]HDk[0x83][0x91][0x14]%9[0x1a]h[0xc0]QJ=EC[0xaa][0x14]U[0xbe][0x9b]0[0xbe][0x5][0x14]M<m[0x6][0x9][0xd5]1[0xa5][0x6]X6[0x15][0xb][0xac]5[0x88][0x15][\r]=[0xeb]U[0xaf]B[0xa8]u[0xbb][0x19][0x10]m[0x90]a[0xb8][0x89]E[\n]"
39 main 2021-02-09 14:09:35,302 DEBUG (Wire.java:73)- http-outgoing-0 << "O[0xd1][0xf7][0xda][0xc1][0xf5][0x1b]g[0xd6][0xd7]7[0xaf][0xa5][0x9d][[0x91][0xb8][0xd6][0xbd][0x8][0x15][0xcc][0xf3][0x1c]b[0x84]c[\n]"
40 main 2021-02-09 14:09:35,302 DEBUG (Wire.java:73)- http-outgoing-0 << "[0x3]"[0xd4]-[0x18] F[0xb2]f[0xca]x[0x13]k[0xed][0xbb][0x86][0x99][0x84][0xfa]{[0xf7]_[0x14][0xbb][0xf][0x87]O?[0x1c]>[0xfd][0xec][0x9f][0xbf]?[0x1f]>[0xff][0xa9]x[0xfc][0xc7][0xde][0x8f][0xbf][0x1e]l[0xdf]sa[0xa5][0xe2][0xc2]2q[0xc0][\r][0x4][0x19][0x0]K[0xd6][0xfb]_[0xab][0xd5]ja[0xec][0x3][0x97][0xb0]>`[0xc4][0xcb][0x15][0x92][0x92][0xaa][0xb1][0xa0]6[0xb1];[0x9c] [0xad]KAoZ[0xad]:[0xd0][0xbd]P[0xa8]t[0x81][0xa8]7[0x7][0x9a]D>pY[0x1a][0x81][0x98][0x11][0x1a][\n]"
41 main 2021-02-09 14:09:35,303 DEBUG (Wire.java:73)- http-outgoing-0 << "[0x9c]i[0xcf][0xa8][0x8c][0x2][0xad][0xb0][0x7]m[0xbc][0xcd]*F[0x9b][0x14][0x96]F0 [0xbd]DD[0xa2][0xdd][0x94]<[0x2]9#&[0xf6]:[0xa7][ [0xa6],[0x8a][0x8d][0xd7][0xee][0xfc][0xdf][0x7].$[0xac][0xef][0x3][0xd7]:[0xb4].[0xca]oY[0x9f][0x10] l[0x98][0xe0]s[0xc8][0xba]f[0x19]Z[0xd2][0x8c][0xcb][0xac][0xae]V[0xcc][0x8][0xa1][0xbc]2.3[0xde][0xc3]"[0xa5][0xa0][0x8f][0x92][0x8c]z[0xed][0xd7][0xeb][0xb2][0x91]N[0xd5]b[0xaf][0xd5][0xb]k[0xb5]7[0xa8]([0xdd][0xef][0x5][0xf2][0xed].[0xad][0x1e]#7[0xdf][0xae]hx[0xad]S[0x86][0xef][0xbb]Z"^[0x87][0xef][0x4][0x11][0xd0]=&M/W[0x8e]_[0x3]0[0xe2]m[0xe5][0x95]iN[0xe][0xab][0xc9][0xa4][0xa9]`@[0x8a]n&[0x94]G[0xb6][0x10][0xa7]N[0x1][0x94][0x19][0x81]E*[0x13]j[0xa8]'[0xc2][0xb0][0x14]Tu=\[0xf9].[0xb4]N[0x17][0xb8][0xe][0xc][0x9f]t]r[0xd7]Y[0x90][0xb2][0x92][0x86][0xce]j[0xe6][0x93][0x1d]>k[0xef][0x80]9?[0xc0][0x85]u?[0xd6][0x9d]Q[0xea][0x16][0xcc]l[0xb2][0xd0]x W![0xe4]4[0xd7][0xe3][0xf6][0xa8][0xf3]v[0xbd]g[0x94]=[0xa8]=[0xa6][0x1c][0xf5][0xfd]W[0xdf]>>[0xf8][0xee][0x99][0xb][0xd1]4[0x84][0x9d]i[0xdb]a1[0x12][0xed]Nw[0x6][0xa3][0x12]N[0xa2]T[0x92]9[0x94]U[0x8]S$[0x17][0xf2]H[0x91][0x9c][0x4](~x\<x[0xb1][0x8][0xa0][0xbf][0xd0][0xbc][0xcf][0x8][0x15][0x93][0x0][0xfb];[0x1f][0x1f][0xfc][0xfc][0xd5]"[0x0][0xc3]h[0x80][0x16][0x82][0x94]'S O[0x9e][0x14]_[0xee]T \h[0xac][0x98]4sx[0xd3][0x83]W]c[0x89][0x88][0x18]oF,<[[0xae][0x96]Q*[0xd7][0x8c]L[0xbc][0xb4]Zf[0xa5][0xed]Rw}[0xa9]s~[0xa9]s~[\n]"
42 main 2021-02-09 14:09:35,303 DEBUG (Wire.java:73)- http-outgoing-0 << "a[0xa9][0x13].u[0xc3][0xf1]l.uI[0xfb][0x90]c[0x89]V3L[0x2][0xef][0xfe][0xb3][0xe2][0xf9]7[0x15]?8A[0xb0]^[0x10][0x81][0xb3][0x94]r[0xd3][0xcc][0x15]3[0xf4][0xf8][0xca][0x88][0xb8][0xf3][0x1f][0x98][0xd7][0xac]3o[0xe5]$[0xa0][0x1c][0xb]B7[0xdf][0xbf]`[0xdf][0x12][0xc1])7[0xc7]s[0xc6][0x89][0xc8][0x9b][0x89][0xc0][0xc8][0xde]CM[0x8b][0x12][0xcc][0x89]5E[\n]"
43 main 2021-02-09 14:09:35,303 DEBUG (Wire.java:73)- http-outgoing-0 << "[0xc7][0xc0][0xf3]<[0xe0]8[0xe0],p[0xce]:`[0x15]8[0xcb][0xce][0x89][0x93][0xc0][0x19][0xc7][0xea][0xb5][0xad]`[0xc5][0xa9][0xe2]uF[0x1];[0xa3][0x91]H[0x2]g"[0xe6][0x95][0x13]kG[0x8f][0x80][0x99][0x9f][0xb]g[0xcb]4[0x1b]g*[0x14][0x85][0x87][0x19][\r][0x14]cX[0x8c]r[0x1a]([0x6][0xca][0x7][0xce]s[0x8][0xd3]2A[0x83]U[0x10]$[0x2]o[0xad]9[0xfe][0xab][0x7]O[0x8a]_[0xbe][0x1f][0xee][0xee][0x14][0xf7][0xb6][0xeb][0x94]O[\r][0xdf][0xf4][0xc][0x86]fC[0xf0][0x99]m[0xee][0x3]W[0x96]oD<[0xd7]E[0xb1]H[0xe9][0x98][0xa4]_[0xdc][0xfe]s[0xb8][0xfb]Eu-,jb[0xa6]&[0x94][0xd7][0x3][0x91][0x19][0xf0][0x8e][0xdd][0xd7][0xc4][0xe4][0xc8][0x13][0x96][0xfe]2[0x16]r[0xb0][0xd6]i[0xb5]O/[0xf3]@[0xcb][0xb5]R[0xaf]Z[0xbe][0xb1][0x93]If[0x6][0xd0][0x1f]>[0xb9][0xf7][0xf5]o[0x15][0x91][0xe2][0xd3];[0xc5][0xcb][0xdb][0xfb][0x8f][0xca][0xeb][0xa1]B[0x98][0xe5]u[0x9d]!>`[0x13](ub[0xb1]l[0x84][0x94][0x92][0x0][0xe1]-[0xff][0xd5]Gw[0xf7]w[0xb6][0x8b][0xbb]w[0xe][0x1e]~2F[0x1a][0xee][0xfe]~a[0xe3][0xf2][0xfe][0xa3][0xed]V[0xb7][0xd5]>[0xdd]-[0xee][0xfe]5[0xf2]`[0x1f][0xd3][0xd7][0xbd][0x9e][0x91][0xb6][0xbd]:[\n]"
44 main 2021-02-09 14:09:35,303 DEBUG (Wire.java:87)- http-outgoing-0 << "yaA[0xa0][0xfd]K`[0xbf][0xf5]?[0xaf][0x3][0x0][0x0][0xff][0xff]0[0xc][0x81][0x9a][0x8b][0x9][0x0][0x0]"
45 main 2021-02-09 14:09:35,306 DEBUG (LoggingManagedHttpClientConnection.java:122)- http-outgoing-0 << HTTP/1.1 200 OK
46 main 2021-02-09 14:09:35,306 DEBUG (LoggingManagedHttpClientConnection.java:125)- http-outgoing-0 << Content-Encoding: gzip
47 main 2021-02-09 14:09:35,306 DEBUG (LoggingManagedHttpClientConnection.java:125)- http-outgoing-0 << Content-Length: 1145
48 main 2021-02-09 14:09:35,306 DEBUG (LoggingManagedHttpClientConnection.java:125)- http-outgoing-0 << Content-Type: text/html
49 main 2021-02-09 14:09:35,306 DEBUG (LoggingManagedHttpClientConnection.java:125)- http-outgoing-0 << Server: bfe
50 main 2021-02-09 14:09:35,306 DEBUG (LoggingManagedHttpClientConnection.java:125)- http-outgoing-0 << Date: Tue, 09 Feb 2021 06:09:34 GMT
51 main 2021-02-09 14:09:35,310 DEBUG (MainClientExec.java:285)- Connection can be kept alive indefinitely
52 main 2021-02-09 14:09:35,316 DEBUG (HttpClientProviderImpl.java:46)- respString: <!DOCTYPE html>
53 <!--STATUS OK--><html> <head><meta http-equiv=content-type content=text/html;charset=utf-8><meta http-equiv=X-UA-Compatible content=IE=Edge><meta content=always name=referrer><link rel=stylesheet type=text/css href=https://ss1.bdstatic.com/5eN1bjq8AAUYm2zgoY3K/r/www/cache/bdorz/baidu.min.css><title>百度一下,你就知道</title></head> <body link=#0000cc> <div id=wrapper> <div id=head> <div class=head_wrapper> <div class=s_form> <div class=s_form_wrapper> <div id=lg> <img hidefocus=true src=//www.baidu.com/img/bd_logo1.png width=270 height=129> </div> <form id=form name=f action=//www.baidu.com/s class=fm> <input type=hidden name=bdorz_come value=1> <input type=hidden name=ie value=utf-8> <input type=hidden name=f value=8> <input type=hidden name=rsv_bp value=1> <input type=hidden name=rsv_idx value=1> <input type=hidden name=tn value=baidu><span class="bg s_ipt_wr"><input id=kw name=wd class=s_ipt value maxlength=255 autocomplete=off autofocus=autofocus></span><span class="bg s_btn_wr"><input type=submit id=su value=百度一下 class="bg s_btn" autofocus></span> </form> </div> </div> <div id=u1> <a href=http://news.baidu.com name=tj_trnews class=mnav>新聞</a> <a href=https://www.hao123.com name=tj_trhao123 class=mnav>hao123</a> <a href=http://map.baidu.com name=tj_trmap class=mnav>地圖</a> <a href=http://v.baidu.com name=tj_trvideo class=mnav>視訊</a> <a href=http://tieba.baidu.com name=tj_trtieba class=mnav>貼吧</a> <noscript> <a href=http://www.baidu.com/bdorz/login.gif?login&amp;tpl=mn&amp;u=http%3A%2F%2Fwww.baidu.com%2f%3fbdorz_come%3d1 name=tj_login class=lb>登入</a> </noscript> <script>document.write('<a href="http://www.baidu.com/bdorz/login.gif?login&tpl=mn&u='+ encodeURIComponent(window.location.href+ (window.location.search === "" ? "?" : "&")+ "bdorz_come=1")+ '" name="tj_login" class="lb">登入</a>');
54                 </script> <a href=//www.baidu.com/more/ name=tj_briicon class=bri style="display: block;">更多產品</a> </div> </div> </div> <div id=ftCon> <div id=ftConw> <p id=lh> <a href=http://home.baidu.com>關於百度</a> <a href=http://ir.baidu.com>About Baidu</a> </p> <p id=cp>&copy;2017&nbsp;Baidu&nbsp;<a href=http://www.baidu.com/duty/>使用百度前必讀</a>&nbsp; <a href=http://jianyi.baidu.com/ class=cp-feedback>意見反饋</a>&nbsp;京ICP證030173號&nbsp; <img src=//www.baidu.com/img/gs.gif> </p> </div> </div> </div> </body> </html>
55 main 2021-02-09 14:09:35,318 DEBUG (PoolingHttpClientConnectionManager.java:344)- Connection [id: 0][route: {s}->https://www.baidu.com:443] can be kept alive indefinitely
56 main 2021-02-09 14:09:35,318 DEBUG (LoggingManagedHttpClientConnection.java:88)- http-outgoing-0: set socket timeout to 0
57 main 2021-02-09 14:09:35,318 DEBUG (PoolingHttpClientConnectionManager.java:351)- Connection released: [id: 0][route: {s}->https://www.baidu.com:443][total available: 1; route allocated: 1 of 50; total allocated: 1 of 200]
58 main 2021-02-09 14:09:35,319 WARN (HttpClientServiceTest.java:80)- requestGet, times: 0, betMils: 403,betNanos: 402522300
httpClientLog

2. 綜合比較

  maxTotal:200

Threads

defaultMaxPerRoute

validateAfterInactivity

betMils

betNanos

 256

 

 

 

 

 20

 1500

 

 

 

 

 5270

5269994599 

 50

 

4310

 

4310753100

 

100

 

4513

 

4512503700

 

150

 

4963

 

4962632601

 

200

 

5245

 

5244764201

 

1024

 

 

 

20

 1500

 

 

5087

 

5087564601

 

50

 

4323

 

4323088301

 

64

  

 

 

 

50

  

 

 

 

15000

 

3900

 

3900514800

 

2000(default)

 

3531

 

3537111900

0

3358

3355394500

32

 

 

50

 

 

0

3471

 

3456438200

 

2000(default)

 

2918

 

2912550800

 50

 

 

 

 

 

 50

 

 

 

 

 

 

15000

 

2847

 

2846675400

0

3190

3189325400

2000(default)

3263

3264044800

1000

2741

2740233600

500

2802

2801841600

800

 

2800

 

2800641400

  

  根據以上資料我們能看出什麼呢?

  在分析資料以前我先羅列一下電腦配置:

  Windows 10 專業版

  Intel(R) Core(TM) i5-7400 CPU @ 3.00GHz 3.00 GHz 4核

  16.0 GB

  64 位

  SSD 232.89 GB

 

  首先根據本機硬體及系統配置,多執行緒可以提高系統的併發效率,但是也不是絕對的,大致在50個執行緒的時候效果較好。然後defaultMaxPerRoute引數也是50,即每個路由預設並行接受的請求數。因為本文的測試目標伺服器是百度,百度伺服器設定連線時間可無限期的保持“Connection can be kept alive indefinitely”,調節validateAfterInactivity引數,總的來說效果不是很大,當然需要排除網路請求的一些影響因素,比如擁塞、路由等情況,可根據下表的引數比較知道,即使相同的配置引數,不同時間點請求伺服器,響應的時長也有差異,也就是說每一次的網路請求都是必然中的一次偶然情況。

3. 同引數比較

  Threads: 50 

  maxTotal: 200

  defaultMaxPerRoute:50

  validateAfterInactivity:1000

  idleTimeOut:3

betMils

betNanos

2813

2813779600

3228

3229129100

3030

3030484200

2806

2805046900

2981

2981383600

2629

2629455200

2814

2813863100

2747

2747754700

2748

2747132800

3365

3365044500

 

  通過上表可以看出,每一次網路請求在同一網路情況下或相似網路情況下,響應的時長差距不大。但因為影響網路的因素眾多,導致每次響應的時長都不一樣。

 四、後記

  暫時先分享到此,後期會有補充的,比如原始碼、架構等。

相關文章