博客
关于我
HttpClient客户端网络编程——高可用、高并发
阅读量:424 次
发布时间:2019-03-06

本文共 34974 字,大约阅读时间需要 116 分钟。

  本文是HttpClient的学习博客,RestTemplate是基于HttpClient的封装,feign可基于HttpClient进行网络通信。

  那么作为较底层的客户端网络编程框架,该怎么配置使其能高可用,高并发,可支持Https协议呢?通读本文也许你会有答案或者启发。

  本文是Maven项目,基于Spring,在本Demo中使用了更方便的SpringBoot。

  以后随着理解HttpClient更深入的时候会不定期更新本博客,也欢迎感兴趣的博友交流和讨论。

一、本文目录

  1. 代码实现

  2. 测试验证

  3. 后记

二、代码实现

1. 项目依赖

1 
2
org.springframework.boot
3
spring-boot-starter
4
5 6
7
8
org.apache.httpcomponents
9
httpclient
10
4.5.12
11
12 13
14
15
com.alibaba
16
fastjson
17
1.2.70
18
19 20
21
org.springframework.boot
22
spring-boot-starter-test
23
test
24
pom.xml

2. 项目结构

  

3. 项目配置

1 #HttpClient配置 2 httpClient: 3   #重试次数 4   retryCount: 3 5   #重启开关 6   requestSentRetryEnabled: true 7   #HttpClient连接池配置 8   pool: 9     #总连接数10     maxTotal: 20011     #每个路由默认连接数,某一个/每服务每次能并行接收的请求数量12     defaultMaxPerRoute: 5013     #Validate connections after 15 sec of inactivity  1500014     validateAfterInactivity: 100015     #idle超时时间16     idleTimeOut: 317     socketCfg:18       #是否立即发送数据,设置为true会关闭Socket缓冲,默认为false19       tcpNoDelay: true20       #是否可以在一个进程关闭Socket后,即使它还没有释放端口,其它进程还可以立即重用端口21       soReuseAddress: true22       #接受数据的等待超时时间,单位ms23       soTimeOut: 50024       #关闭Socket时,要么发送完所有数据,要么等待60s后,就关闭连接,此时socket.close()是阻塞的25       soLinger: 6026       #开启监视TCP连接是否有效27       soKeepAlive: true
application.yml

4. HttpClient客户端配置类

  通过配置连接池管理对象PoolingHttpClientConnectionManager,设置两个重要参数maxTotal和defaultMaxPerRoute,和其它参数。本文参数配置参考,文档上面的参数更多更齐全,包括HttpConnectionFactory、DnsResolver、ConnectionConfig、RequestConfig、RequestConfig、HttpClientContext、设置代理。这些参数本文没有配置,使用HttpClient的默认配置,感兴趣想继续深入研究的可以去学习了解。BackoffManager可以在连接池处于闲暇时进行收缩,不过网络上资料较少,目前还没研究出怎么使用和配置。

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
socketFactoryRegistry = RegistryBuilder.
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工具类

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的HttpEntity111      *112      * @param response 响应实体113      * @return HttpEntity114      */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      * 获取ContentType132      *133      * @param httpEntity HttpEntity134      * @return ContentType135      */136     public static String getContentType(final HttpEntity httpEntity) {137         return httpEntity.getContentType().getValue();138     }139 140     /**141      * 获取ContentEncoding142      *143      * @param httpEntity HttpEntity144      * @return ContentEncoding145      */146     public static String getContentEncoding(final HttpEntity httpEntity) {147         return httpEntity.getContentEncoding().getValue();148     }149 150     /**151      * 获取响应体对象InputStream152      *153      * @param httpEntity HttpEntity154      * @return 获取响应体对象InputStream155      * @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 InputStream165      * @return 获取响应体对象InputStream166      * @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 HttpEntity177      * @return 获取响应体对象InputStream178      * @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      * 字符串转InputStream187      *188      * @param str 字符串189      * @return inputStream190      */191     public static InputStream stringTransferToInputStream(String str) {192         return new ByteArrayInputStream(str.getBytes());193     }194 195     /**196      * 设置默认的Content-Type197      *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      * 设置Authorization206      *207      * @param request http请求对象208      * @param token   token209      */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   token219      */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 inputStream230      */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业务接口及其实现

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

 

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/0813  */14 @Service15 public class HttpClientServiceImpl implements HttpClientService {16 17     @Autowired18     private HttpClientProvider httpClientProvider;19 20 21     @Override22     public void requestGet(String url) {23         httpClientProvider.requestGet(url);24     }25 26     @Override27     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的接口,丝毫不会受到影响。所以编程原则里有一条:面向接口编程,面向抽象编程。

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

 

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/0826  */27 @Service28 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     @Override36     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     @Override59     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业务接口测试

  通过运行单元测试类来测试接口,下文有详细的测试数据。

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 @SpringBootTest16 class HttpClientServiceTest {17     private final static Logger logger = LoggerFactory.getLogger(HttpClientServiceTest.class);18 19     @Autowired20     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      * workStealingPool28      */29     public final static ExecutorService workStealingPool = Executors.newWorkStealingPool(1<<6);30 31     @Test32     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     @Test47     void requestPost() {48     }49 }
HttpClientServiceTest.java

 

三、测试验证

  本文测试方式可能不是那么专业与严谨,可是并不妨碍我们通过测试数据看出些原理,总结出些规律,也许有些片面,但可看出些趋势。

1. 运行日志

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 established10 main 2021-02-09 14:09:35,250 DEBUG (SSLConnectionSocketFactory.java:466)-  negotiated protocol: TLSv1.211 main 2021-02-09 14:09:35,250 DEBUG (SSLConnectionSocketFactory.java:467)-  negotiated cipher suite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA25612 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=CN13 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=BE15 main 2021-02-09 14:09:35,254 DEBUG (DefaultHttpClientConnectionOperator.java:146)- Connection established 192.168.100.24:63820<->14.215.177.38:44316 main 2021-02-09 14:09:35,255 DEBUG (MainClientExec.java:255)- Executing request GET / HTTP/1.117 main 2021-02-09 14:09:35,255 DEBUG (MainClientExec.java:260)- Target auth state: UNCHALLENGED18 main 2021-02-09 14:09:35,255 DEBUG (MainClientExec.java:266)- Proxy auth state: UNCHALLENGED19 main 2021-02-09 14:09:35,257 DEBUG (LoggingManagedHttpClientConnection.java:133)- http-outgoing-0 >> GET / HTTP/1.120 main 2021-02-09 14:09:35,257 DEBUG (LoggingManagedHttpClientConnection.java:136)- http-outgoing-0 >> Host: www.baidu.com21 main 2021-02-09 14:09:35,257 DEBUG (LoggingManagedHttpClientConnection.java:136)- http-outgoing-0 >> Connection: Keep-Alive22 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,deflate24 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
[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\
[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 OK46 main 2021-02-09 14:09:35,306 DEBUG (LoggingManagedHttpClientConnection.java:125)- http-outgoing-0 << Content-Encoding: gzip47 main 2021-02-09 14:09:35,306 DEBUG (LoggingManagedHttpClientConnection.java:125)- http-outgoing-0 << Content-Length: 114548 main 2021-02-09 14:09:35,306 DEBUG (LoggingManagedHttpClientConnection.java:125)- http-outgoing-0 << Content-Type: text/html49 main 2021-02-09 14:09:35,306 DEBUG (LoggingManagedHttpClientConnection.java:125)- http-outgoing-0 << Server: bfe50 main 2021-02-09 14:09:35,306 DEBUG (LoggingManagedHttpClientConnection.java:125)- http-outgoing-0 << Date: Tue, 09 Feb 2021 06:09:34 GMT51 main 2021-02-09 14:09:35,310 DEBUG (MainClientExec.java:285)- Connection can be kept alive indefinitely52 main 2021-02-09 14:09:35,316 DEBUG (HttpClientProviderImpl.java:46)- respString: 53
百度一下,你就知道

关于百度 About Baidu

©2017 Baidu 使用百度前必读  意见反馈 京ICP证030173号 

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 indefinitely56 main 2021-02-09 14:09:35,318 DEBUG (LoggingManagedHttpClientConnection.java:88)- http-outgoing-0: set socket timeout to 057 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

 

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

 四、后记

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

转载地址:http://kywyz.baihongyu.com/

你可能感兴趣的文章
看完你就明白的锁系列之锁的状态
查看>>
我的价值观
查看>>
值类型与引用类型(中)
查看>>
QBlog V2.5 源码开放下载(ASP.NET 番外系列之开端)
查看>>
秋色园引发CPU百分百命案的事件分析与总结
查看>>
稀疏数组
查看>>
js的严格模式
查看>>
Oracle VM VirtualBox安装PVE虚拟机
查看>>
Android MediaPlayer setDataSource failed
查看>>
ASP.NET Core 实战:Linux 小白的 .NET Core 部署之路
查看>>
虚拟机搭建hadoop环境
查看>>
DataStax Bulk Loader教程(四)
查看>>
.NET应用框架架构设计实践 - 概述
查看>>
Java基础IO流(一)
查看>>
Hibernate入门(四)---------一级缓存
查看>>
一个web前端开发者的日常唠叨
查看>>
内存分配-slab分配器
查看>>
技术写作技巧分享:我是如何从写作小白成长为多平台优秀作者的?
查看>>
[Python学习笔记]组织文件
查看>>
基于Redo Log和Undo Log的MySQL崩溃恢复流程
查看>>