• 欢迎访问开心洋葱网站,在线教程,推荐使用最新版火狐浏览器和Chrome浏览器访问本网站,欢迎加入开心洋葱 QQ群
  • 为方便开心洋葱网用户,开心洋葱官网已经开启复制功能!
  • 欢迎访问开心洋葱网站,手机也能访问哦~欢迎加入开心洋葱多维思维学习平台 QQ群
  • 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏开心洋葱吧~~~~~~~~~~~~~!
  • 由于近期流量激增,小站的ECS没能经的起亲们的访问,本站依然没有盈利,如果各位看如果觉着文字不错,还请看官给小站打个赏~~~~~~~~~~~~~!

HttpClient客户端网络编程——高可用、高并发

JAVA相关 复姓江山 2394次浏览 0个评论

  本文是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. 项目结构

  HttpClient客户端网络编程——高可用、高并发

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

 

  通过上表可以看出,每一次网络请求在同一网络情况下或相似网络情况下,响应的时长差距不大。但因为影响网络的因素众多,导致每次响应的时长都不一样。

 四、后记

  暂时先分享到此,后期会有补充的,比如源码、架构等。


开心洋葱 , 版权所有丨如未注明 , 均为原创丨未经授权请勿修改 , 转载请注明HttpClient客户端网络编程——高可用、高并发
喜欢 (0)

您必须 登录 才能发表评论!

加载中……