引入依赖

  1. <dependency>
  2. <groupId>org.apache.httpcomponents</groupId>
  3. <artifactId>httpclient</artifactId>
  4. <version>4.5.13</version>
  5. </dependency>

参数配置

连接池参数:

配置 简介 默认值
maxConnTotal 最大连接数 20
maxConnPerRoute 分配给同一个route(路由)最大的并发数
举例来说,我们使用HttpClient的实现来分别请求 www.baidu.com 的资源和 www.bing.com 的资源那么他就会产生两个route
2
connTimeToLive 连接池中connection的存活时间 永久有效
maxIdleTime 最大空闲时间 如果没有指定maxIdleTime的话,但是有设置evictExpiredConnections的话,默认是10秒

RequestConfig常用参数:

重要参数 默认值 特殊值(0)





超时参数
socketTimeout 指客户端从服务器读取数据的timeout,超出后会抛出SocketTimeOutException -1,未定义使用默认值 无限超时
connectTimeout 客户端和服务器建立连接的timeout,就是http请求的三个阶段,一:建立连接;二:数据传送;三,断开连接。超时后会ConnectionTimeOutException -1,未定义使用默认值 无限超时
connectionRequestTimeout 从连接池获取连接的timeout。
HttpClient中的要用连接时尝试从连接池中获取,若是在等待了一定的时间后还没有获取到可用连接(比如连接池中没有空闲连接了)则会抛出获取连接超时异常。
-1,未定义使用默认值 无限超时

高并发下性能优化-http连接池

首先,明确两点:

1.http连接池不是万能的,过多的长连接会占用服务器资源,导致其他服务受阻
2.http连接池只适用于请求是经常访问同一主机(或同一个接口)的情况下
3.并发数不高的情况下资源利用率低下

使用http连接池的优点:

1.复用http连接,省去了tcp的3次握手和4次挥手的时间,极大降低请求响应的时间
2.自动管理tcp连接,不用人为地释放/创建连接

使用http连接池的大致流程 :

1.创建PoolingHttpClientConnectionManager实例
2.给manager设置参数
3.给manager设置重试策略
4.给manager设置连接管理策略
5.开启监控线程,及时关闭被服务器单向断开的连接
6.构建HttpClient实例
7.创建HttpPost/HttpGet实例,并设置参数
8.获取响应,做适当的处理
9.将用完的连接放回连接池

使用方式:

java原生使用方式:

  1. package com.powerX.httpClient;
  2. import java.io.BufferedReader;
  3. import java.io.IOException;
  4. import java.io.InputStream;
  5. import java.io.InputStreamReader;
  6. import java.io.OutputStream;
  7. import java.net.HttpURLConnection;
  8. import java.net.MalformedURLException;
  9. import java.net.URL;
  10. public class HttpClient {
  11. public static String doGet(String httpurl) {
  12. HttpURLConnection connection = null;
  13. InputStream is = null;
  14. BufferedReader br = null;
  15. String result = null;// 返回结果字符串
  16. try {
  17. // 创建远程url连接对象
  18. URL url = new URL(httpurl);
  19. // 通过远程url连接对象打开一个连接,强转成httpURLConnection类
  20. connection = (HttpURLConnection) url.openConnection();
  21. // 设置连接方式:get
  22. connection.setRequestMethod("GET");
  23. // 设置连接主机服务器的超时时间:15000毫秒
  24. connection.setConnectTimeout(15000);
  25. // 设置读取远程返回的数据时间:60000毫秒
  26. connection.setReadTimeout(60000);
  27. // 发送请求
  28. connection.connect();
  29. // 通过connection连接,获取输入流
  30. if (connection.getResponseCode() == 200) {
  31. is = connection.getInputStream();
  32. // 封装输入流is,并指定字符集
  33. br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
  34. // 存放数据
  35. StringBuffer sbf = new StringBuffer();
  36. String temp = null;
  37. while ((temp = br.readLine()) != null) {
  38. sbf.append(temp);
  39. sbf.append("\r\n");
  40. }
  41. result = sbf.toString();
  42. }
  43. } catch (MalformedURLException e) {
  44. e.printStackTrace();
  45. } catch (IOException e) {
  46. e.printStackTrace();
  47. } finally {
  48. // 关闭资源
  49. if (null != br) {
  50. try {
  51. br.close();
  52. } catch (IOException e) {
  53. e.printStackTrace();
  54. }
  55. }
  56. if (null != is) {
  57. try {
  58. is.close();
  59. } catch (IOException e) {
  60. e.printStackTrace();
  61. }
  62. }
  63. connection.disconnect();// 关闭远程连接
  64. }
  65. return result;
  66. }
  67. public static String doPost(String httpUrl, String param) {
  68. HttpURLConnection connection = null;
  69. InputStream is = null;
  70. OutputStream os = null;
  71. BufferedReader br = null;
  72. String result = null;
  73. try {
  74. URL url = new URL(httpUrl);
  75. // 通过远程url连接对象打开连接
  76. connection = (HttpURLConnection) url.openConnection();
  77. // 设置连接请求方式
  78. connection.setRequestMethod("POST");
  79. // 设置连接主机服务器超时时间:15000毫秒
  80. connection.setConnectTimeout(15000);
  81. // 设置读取主机服务器返回数据超时时间:60000毫秒
  82. connection.setReadTimeout(60000);
  83. // 默认值为:false,当向远程服务器传送数据/写数据时,需要设置为true
  84. connection.setDoOutput(true);
  85. // 默认值为:true,当前向远程服务读取数据时,设置为true,该参数可有可无
  86. connection.setDoInput(true);
  87. // 设置传入参数的格式:请求参数应该是 name1=value1&name2=value2 的形式。
  88. connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
  89. // 设置鉴权信息:Authorization: Bearer da3efcbf-0845-4fe3-8aba-ee040be542c0
  90. connection.setRequestProperty("Authorization", "Bearer da3efcbf-0845-4fe3-8aba-ee040be542c0");
  91. // 通过连接对象获取一个输出流
  92. os = connection.getOutputStream();
  93. // 通过输出流对象将参数写出去/传输出去,它是通过字节数组写出的
  94. os.write(param.getBytes());
  95. // 通过连接对象获取一个输入流,向远程读取
  96. if (connection.getResponseCode() == 200) {
  97. is = connection.getInputStream();
  98. // 对输入流对象进行包装:charset根据工作项目组的要求来设置
  99. br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
  100. StringBuffer sbf = new StringBuffer();
  101. String temp = null;
  102. // 循环遍历一行一行读取数据
  103. while ((temp = br.readLine()) != null) {
  104. sbf.append(temp);
  105. sbf.append("\r\n");
  106. }
  107. result = sbf.toString();
  108. }
  109. } catch (MalformedURLException e) {
  110. e.printStackTrace();
  111. } catch (IOException e) {
  112. e.printStackTrace();
  113. } finally {
  114. // 关闭资源
  115. if (null != br) {
  116. try {
  117. br.close();
  118. } catch (IOException e) {
  119. e.printStackTrace();
  120. }
  121. }
  122. if (null != os) {
  123. try {
  124. os.close();
  125. } catch (IOException e) {
  126. e.printStackTrace();
  127. }
  128. }
  129. if (null != is) {
  130. try {
  131. is.close();
  132. } catch (IOException e) {
  133. e.printStackTrace();
  134. }
  135. }
  136. // 断开与远程地址url的连接
  137. connection.disconnect();
  138. }
  139. return result;
  140. }
  141. }

apache httpClient4.5使用方式:

  1. package com.powerX.httpClient;
  2. import java.io.IOException;
  3. import java.io.UnsupportedEncodingException;
  4. import java.util.ArrayList;
  5. import java.util.Iterator;
  6. import java.util.List;
  7. import java.util.Map;
  8. import java.util.Map.Entry;
  9. import java.util.Set;
  10. import org.apache.http.HttpEntity;
  11. import org.apache.http.NameValuePair;
  12. import org.apache.http.client.ClientProtocolException;
  13. import org.apache.http.client.config.RequestConfig;
  14. import org.apache.http.client.entity.UrlEncodedFormEntity;
  15. import org.apache.http.client.methods.CloseableHttpResponse;
  16. import org.apache.http.client.methods.HttpGet;
  17. import org.apache.http.client.methods.HttpPost;
  18. import org.apache.http.impl.client.CloseableHttpClient;
  19. import org.apache.http.impl.client.HttpClients;
  20. import org.apache.http.message.BasicNameValuePair;
  21. import org.apache.http.util.EntityUtils;
  22. public class HttpClient4 {
  23. public static String doGet(String url) {
  24. CloseableHttpClient httpClient = null;
  25. CloseableHttpResponse response = null;
  26. String result = "";
  27. try {
  28. // 通过址默认配置创建一个httpClient实例
  29. httpClient = HttpClients.createDefault();
  30. // 创建httpGet远程连接实例
  31. HttpGet httpGet = new HttpGet(url);
  32. // 设置请求头信息,鉴权
  33. httpGet.setHeader("Authorization", "Bearer da3efcbf-0845-4fe3-8aba-ee040be542c0");
  34. // 设置配置请求参数
  35. RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(35000)// 连接主机服务超时时间
  36. .setConnectionRequestTimeout(35000)// 请求超时时间
  37. .setSocketTimeout(60000)// 数据读取超时时间
  38. .build();
  39. // 为httpGet实例设置配置
  40. httpGet.setConfig(requestConfig);
  41. // 执行get请求得到返回对象
  42. response = httpClient.execute(httpGet);
  43. // 通过返回对象获取返回数据
  44. HttpEntity entity = response.getEntity();
  45. // 通过EntityUtils中的toString方法将结果转换为字符串
  46. result = EntityUtils.toString(entity);
  47. } catch (ClientProtocolException e) {
  48. e.printStackTrace();
  49. } catch (IOException e) {
  50. e.printStackTrace();
  51. } finally {
  52. // 关闭资源
  53. if (null != response) {
  54. try {
  55. response.close();
  56. } catch (IOException e) {
  57. e.printStackTrace();
  58. }
  59. }
  60. if (null != httpClient) {
  61. try {
  62. httpClient.close();
  63. } catch (IOException e) {
  64. e.printStackTrace();
  65. }
  66. }
  67. }
  68. return result;
  69. }
  70. public static String doPost(String url, Map<String, Object> paramMap) {
  71. CloseableHttpClient httpClient = null;
  72. CloseableHttpResponse httpResponse = null;
  73. String result = "";
  74. // 创建httpClient实例
  75. httpClient = HttpClients.createDefault();
  76. // 创建httpPost远程连接实例
  77. HttpPost httpPost = new HttpPost(url);
  78. // 配置请求参数实例
  79. RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(35000)// 设置连接主机服务超时时间
  80. .setConnectionRequestTimeout(35000)// 设置连接请求超时时间
  81. .setSocketTimeout(60000)// 设置读取数据连接超时时间
  82. .build();
  83. // 为httpPost实例设置配置
  84. httpPost.setConfig(requestConfig);
  85. // 设置请求头
  86. httpPost.addHeader("Content-Type", "application/x-www-form-urlencoded");
  87. // 封装post请求参数
  88. if (null != paramMap && paramMap.size() > 0) {
  89. List<NameValuePair> nvps = new ArrayList<NameValuePair>();
  90. // 通过map集成entrySet方法获取entity
  91. Set<Entry<String, Object>> entrySet = paramMap.entrySet();
  92. // 循环遍历,获取迭代器
  93. Iterator<Entry<String, Object>> iterator = entrySet.iterator();
  94. while (iterator.hasNext()) {
  95. Entry<String, Object> mapEntry = iterator.next();
  96. nvps.add(new BasicNameValuePair(mapEntry.getKey(), mapEntry.getValue().toString()));
  97. }
  98. // 为httpPost设置封装好的请求参数
  99. try {
  100. httpPost.setEntity(new UrlEncodedFormEntity(nvps, "UTF-8"));
  101. } catch (UnsupportedEncodingException e) {
  102. e.printStackTrace();
  103. }
  104. }
  105. try {
  106. // httpClient对象执行post请求,并返回响应参数对象
  107. httpResponse = httpClient.execute(httpPost);
  108. // 从响应对象中获取响应内容
  109. HttpEntity entity = httpResponse.getEntity();
  110. result = EntityUtils.toString(entity);
  111. } catch (ClientProtocolException e) {
  112. e.printStackTrace();
  113. } catch (IOException e) {
  114. e.printStackTrace();
  115. } finally {
  116. // 关闭资源
  117. if (null != httpResponse) {
  118. try {
  119. httpResponse.close();
  120. } catch (IOException e) {
  121. e.printStackTrace();
  122. }
  123. }
  124. if (null != httpClient) {
  125. try {
  126. httpClient.close();
  127. } catch (IOException e) {
  128. e.printStackTrace();
  129. }
  130. }
  131. }
  132. return result;
  133. }
  134. }

springboot使用方式:

连接池方式:

统一配置参数、设置默认值:

  1. import lombok.Data;
  2. import lombok.experimental.Accessors;
  3. import org.apache.commons.lang3.ObjectUtils;
  4. import java.io.Serializable;
  5. /**
  6. * @author yaol
  7. */
  8. @Data
  9. @Accessors(chain = true)
  10. public class HttpClientProperties implements Serializable {
  11. /**
  12. * 唯一标识, 日志辅助使用
  13. */
  14. private String flag;
  15. /**
  16. * 代理ip, null 默认不使用代理
  17. */
  18. private String httpProxyHost;
  19. /**
  20. * 代理端口,null 默认不使用代理
  21. */
  22. private Integer httpProxyPort;
  23. /**
  24. * 从连接池获取连接的timeout。 / millisecond
  25. */
  26. private Integer connectionRequestTimeout;
  27. /**
  28. * 客户端和服务器建立连接的timeout / millisecond
  29. */
  30. private Integer connectTimeout;
  31. /**
  32. * 客户端从服务器读取数据的timeout / millisecond
  33. */
  34. private Integer readTimeout;
  35. /**
  36. * 最大空闲连接时间 / millisecond
  37. */
  38. private Integer maxIdleTime;
  39. /**
  40. * 服务最大连接数
  41. */
  42. private Integer maxConnTotal;
  43. /**
  44. * 分配给同一个route(路由)最大的并发数
  45. */
  46. private Integer maxConnPerRoute;
  47. /**
  48. * 是否需要启用证书验证
  49. */
  50. private Boolean needSslValidation;
  51. /**
  52. * ssl签名证书路径
  53. */
  54. private String sslCertPath;
  55. /**
  56. * ssl签名证书密码
  57. */
  58. private String sslCertPassword;
  59. /**
  60. * 异常重试次数(null or <0: 不会重实)
  61. */
  62. private Integer retryTimes;
  63. /**
  64. * 异常重试间隔(null or <=0: 直接重试,不做间隔) / millisecond
  65. */
  66. private Integer retryIntervalTime;
  67. /**
  68. * 默认值设置
  69. */
  70. public void buildDefault() {
  71. this.connectTimeout = getConnectTimeout();
  72. this.readTimeout = getReadTimeout();
  73. this.connectionRequestTimeout = getConnectionRequestTimeout();
  74. this.maxIdleTime = getMaxIdleTime();
  75. this.maxConnTotal = getMaxConnTotal();
  76. this.maxConnPerRoute = getMaxConnPerRoute();
  77. this.needSslValidation = getNeedSslValidation();
  78. this.retryTimes = getRetryTimes();
  79. this.retryIntervalTime = getRetryIntervalTime();
  80. }
  81. public Integer getConnectTimeout() {
  82. return ObjectUtils.defaultIfNull(this.connectTimeout, 10 * 1000);
  83. }
  84. public Integer getReadTimeout() {
  85. return ObjectUtils.defaultIfNull(this.readTimeout, 10 * 1000);
  86. }
  87. public Integer getConnectionRequestTimeout() {
  88. return ObjectUtils.defaultIfNull(this.connectionRequestTimeout, 3 * 1000);
  89. }
  90. public Integer getMaxIdleTime() {
  91. return ObjectUtils.defaultIfNull(this.maxIdleTime, 20 * 1000);
  92. }
  93. public Integer getMaxConnTotal() {
  94. return ObjectUtils.defaultIfNull(this.maxConnTotal, 200);
  95. }
  96. public Integer getMaxConnPerRoute() {
  97. return ObjectUtils.defaultIfNull(this.maxConnPerRoute, 100);
  98. }
  99. public Boolean getNeedSslValidation() {
  100. return ObjectUtils.defaultIfNull(this.needSslValidation, false);
  101. }
  102. public Integer getRetryTimes() {
  103. return ObjectUtils.defaultIfNull(this.retryTimes, 0);
  104. }
  105. public Integer getRetryIntervalTime() {
  106. return ObjectUtils.defaultIfNull(this.retryIntervalTime, 100);
  107. }
  108. }

ClientHttpRequestFactory工厂类工具类:

  1. import com.alibaba.fastjson.JSON;
  2. import com.pingxx.common.consts.LogConsts;
  3. import com.pingxx.common.encryption.CertUtil;
  4. import com.pingxx.common.enums.LogTypeEnum;
  5. import lombok.extern.slf4j.Slf4j;
  6. import org.apache.commons.lang3.StringUtils;
  7. import org.apache.http.HttpHost;
  8. import org.apache.http.conn.ssl.NoopHostnameVerifier;
  9. import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
  10. import org.apache.http.impl.client.CloseableHttpClient;
  11. import org.apache.http.impl.client.HttpClientBuilder;
  12. import org.apache.http.impl.client.HttpClients;
  13. import org.apache.http.ssl.SSLContextBuilder;
  14. import org.apache.http.ssl.SSLContexts;
  15. import org.apache.http.ssl.TrustStrategy;
  16. import org.springframework.http.client.ClientHttpRequestFactory;
  17. import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
  18. import javax.net.ssl.SSLContext;
  19. import java.security.GeneralSecurityException;
  20. import java.security.KeyStore;
  21. import java.util.concurrent.TimeUnit;
  22. import static net.logstash.logback.marker.Markers.append;
  23. /**
  24. * 统一ClientHttpRequestFactory
  25. * @author yaol
  26. */
  27. @Slf4j
  28. public class ClientHttpRequestFactoryUtil {
  29. /**
  30. * 统一ClientHttpRequestFactory
  31. * @param httpClientProperties
  32. * @return
  33. */
  34. public static ClientHttpRequestFactory requestFactory(HttpClientProperties httpClientProperties) {
  35. log.info("{} httpClientProperties: {}", httpClientProperties.getFlag(), JSON.toJSONString(httpClientProperties));
  36. try {
  37. HttpClientBuilder httpClientBuilder = HttpClients.custom();
  38. if (Boolean.TRUE.equals(httpClientProperties.getNeedSslValidation()) &&
  39. StringUtils.isNotBlank(httpClientProperties.getSslCertPath())
  40. ) {
  41. //设置证书
  42. KeyStore keyStore = CertUtil.getKeyStore(httpClientProperties.getSslCertPath(),
  43. httpClientProperties.getSslCertPassword(), CertUtil.KEY_STORE_TYPE_PKCS12);
  44. if (keyStore != null) {
  45. SSLContextBuilder sslContextBuilder = SSLContexts.custom();
  46. try {
  47. sslContextBuilder.loadKeyMaterial(keyStore, httpClientProperties.getSslCertPassword().toCharArray())
  48. .loadTrustMaterial(keyStore, (chain, authType) -> true);
  49. SSLContext sslContext = sslContextBuilder
  50. .loadTrustMaterial(keyStore, (chain, authType) -> true)
  51. .build();
  52. SSLConnectionSocketFactory connectionSocketFactory = new SSLConnectionSocketFactory(
  53. sslContext, NoopHostnameVerifier.INSTANCE);
  54. httpClientBuilder.setSSLSocketFactory(connectionSocketFactory);
  55. } catch (GeneralSecurityException e) {
  56. log.error(append(LogConsts.LOG_TYPE, LogTypeEnum.EXCEPTION),
  57. "init SSLContext error:{}", httpClientProperties.getFlag(), e);
  58. throw new RuntimeException("init SSLContext error");
  59. }
  60. } else {
  61. log.error("HttpClient need ssl cert, but keyStore is null:{}", httpClientProperties.getFlag());
  62. throw new RuntimeException("HttpClient need ssl cert, but keyStore is null");
  63. }
  64. } else {
  65. //证书绕过
  66. TrustStrategy acceptingTrustStrategy = (x509Certificates, authType) -> true;
  67. SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy).build();
  68. SSLConnectionSocketFactory connectionSocketFactory = new SSLConnectionSocketFactory(sslContext,
  69. new NoopHostnameVerifier());
  70. httpClientBuilder.setSSLSocketFactory(connectionSocketFactory);
  71. }
  72. //代理
  73. if (StringUtils.isNotBlank(httpClientProperties.getHttpProxyHost()) && httpClientProperties.getHttpProxyPort() != null) {
  74. httpClientBuilder.setProxy(new HttpHost(httpClientProperties.getHttpProxyHost(), httpClientProperties.getHttpProxyPort(), "http"));
  75. }
  76. // 只有io异常才会触发重试
  77. HttpRequestRetryHandler handler = new HttpRequestRetryHandler() {
  78. @Override
  79. public boolean retryRequest(IOException exception, int curRetryCount, HttpContext context) {
  80. // curRetryCount 每一次都会递增,从1开始
  81. if (curRetryCount > retryTimes) {
  82. return false;
  83. }
  84. try {
  85. //重试延迟
  86. if (retryTimes > 0 && retryIntervalTime > 0 ) {
  87. Thread.sleep(curRetryCount * retryIntervalTime);
  88. }
  89. } catch (InterruptedException e) {
  90. throw new RuntimeException(e);
  91. }
  92. //重试场景
  93. if (exception instanceof ConnectTimeoutException
  94. || exception instanceof NoHttpResponseException
  95. || exception instanceof ConnectException
  96. // || exception instanceof UnknownHostException
  97. ) {
  98. return true;
  99. }
  100. HttpClientContext clientContext = HttpClientContext.adapt(context);
  101. org.apache.http.HttpRequest request = clientContext.getRequest();
  102. boolean idempotent = !(request instanceof HttpEntityEnclosingRequest);
  103. if (idempotent) {
  104. // 如果请求被认为是幂等的,那么就重试。即重复执行不影响程序其他效果的
  105. return true;
  106. }
  107. return false;
  108. }
  109. };
  110. //重试handler
  111. httpClientBuilder.setRetryHandler(handler);
  112. //最大连接数
  113. httpClientBuilder.setMaxConnTotal(httpClientProperties.getMaxConnTotal());
  114. //分配给同一个route(路由)最大的并发数
  115. httpClientBuilder.setMaxConnPerRoute(httpClientProperties.getMaxConnPerRoute());
  116. //最大空闲连接时间
  117. httpClientBuilder.evictExpiredConnections()
  118. .evictIdleConnections(httpClientProperties.getMaxIdleTime(), TimeUnit.MILLISECONDS);
  119. HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
  120. //从连接池获取连接的timeout。
  121. requestFactory.setConnectionRequestTimeout(httpClientProperties.getConnectionRequestTimeout());
  122. //客户端和服务器建立连接的timeout
  123. requestFactory.setConnectTimeout(httpClientProperties.getConnectTimeout());
  124. //指客户端从服务器读取数据的timeout
  125. requestFactory.setReadTimeout(httpClientProperties.getReadTimeout());
  126. CloseableHttpClient httpClient = httpClientBuilder.build();
  127. requestFactory.setHttpClient(httpClient);
  128. return requestFactory;
  129. } catch (Exception e) {
  130. log.error("new ClientHttpRequestFactory exception: {}", httpClientProperties.getFlag(), e);
  131. throw new RuntimeException("new ClientHttpRequestFactory exception", e);
  132. }
  133. }
  134. }

获取nacos配置、初始化restTemplate:

  1. import com.pingxx.common.util.http.ClientHttpRequestFactoryUtil;
  2. import com.pingxx.common.util.http.HttpClientProperties;
  3. import lombok.Getter;
  4. import lombok.Setter;
  5. import lombok.extern.slf4j.Slf4j;
  6. import org.springframework.boot.context.properties.ConfigurationProperties;
  7. import org.springframework.boot.context.properties.EnableConfigurationProperties;
  8. import org.springframework.boot.web.client.RestTemplateBuilder;
  9. import org.springframework.context.annotation.Bean;
  10. import org.springframework.context.annotation.Configuration;
  11. import org.springframework.http.client.ClientHttpRequestFactory;
  12. import org.springframework.web.client.RestTemplate;
  13. @Getter
  14. @Setter
  15. @Slf4j
  16. @EnableConfigurationProperties
  17. @Configuration
  18. @ConfigurationProperties(prefix = "http-client")
  19. public class HttpClientConfig {
  20. private String httpProxyHost;
  21. private Integer httpProxyPort;
  22. private Integer connectionRequestTimeout;
  23. private Integer connectTimeout;
  24. private Integer readTimeout;
  25. private Integer maxIdleTime;
  26. private Integer maxConnTotal;
  27. private Integer maxConnPerRoute;
  28. @Bean
  29. public ClientHttpRequestFactory requestFactory() {
  30. HttpClientProperties httpClientProperties = new HttpClientProperties()
  31. .setHttpProxyHost(httpProxyHost)
  32. .setHttpProxyPort(httpProxyPort)
  33. .setConnectionRequestTimeout(connectionRequestTimeout)
  34. .setConnectTimeout(connectTimeout)
  35. .setReadTimeout(readTimeout)
  36. .setMaxIdleTime(maxIdleTime)
  37. .setMaxConnTotal(maxConnTotal)
  38. .setMaxConnPerRoute(maxConnPerRoute)
  39. .setFlag("common");
  40. httpClientProperties.buildDefault();
  41. return ClientHttpRequestFactoryUtil.requestFactory(httpClientProperties);
  42. }
  43. @Bean
  44. public RestTemplate restTemplate(RestTemplateBuilder builder) {
  45. return builder
  46. .requestFactory(this::requestFactory)
  47. .build();
  48. }
  49. }

发起请求工具类:

  1. package com.example.boothttp.http;
  2. import lombok.extern.slf4j.Slf4j;
  3. import org.slf4j.Marker;
  4. import org.springframework.http.*;
  5. import org.springframework.stereotype.Component;
  6. import org.springframework.web.client.RestTemplate;
  7. import static net.logstash.logback.marker.Markers.append;
  8. /**
  9. * @author yaol
  10. */
  11. @Slf4j
  12. @Component
  13. public class HttpRequestHelper {
  14. public HttpResp send(HttpReq reqArgs, RestTemplate restTemplate) {
  15. String url = reqArgs.getUrl();
  16. HttpHeaders reqHeaders = reqArgs.getReqHeaders();
  17. String reqBody = reqArgs.getReqBody();
  18. if (reqArgs.getReqHeaders() == null) {
  19. reqHeaders = new HttpHeaders();
  20. }
  21. //设置默认请求头
  22. reqHeaders.setContentType(MediaType.APPLICATION_JSON);
  23. reqHeaders.add(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE);
  24. HttpEntity<String> requestEntity = new HttpEntity<>(reqBody, reqHeaders);
  25. long requestStartAt = System.currentTimeMillis();
  26. String responseBody = "";
  27. int statusCode = 0;
  28. HttpHeaders responseHeaders = null;
  29. long costMs;
  30. try {
  31. ResponseEntity<String> responseEntity = restTemplate.postForEntity(url, requestEntity, String.class);
  32. statusCode = responseEntity.getStatusCodeValue();
  33. responseBody = responseEntity.getBody();
  34. responseHeaders = responseEntity.getHeaders();
  35. } finally {
  36. costMs = System.currentTimeMillis() - requestStartAt;
  37. if (reqBody.length() > 2048) {
  38. reqBody = reqBody.substring(0, 2048);
  39. }
  40. //todo log_type
  41. Marker marker = append("log_type", "third")
  42. .and(append("cost_ms", costMs))
  43. .and(append("request_url", url))
  44. .and(append("status_code", statusCode))
  45. .and(append("re_body", reqBody))
  46. .and(append("resp_body", responseBody))
  47. .and(append("req_headers", reqHeaders.toString()))
  48. .and(append("reap_headers", responseHeaders != null ? responseHeaders.toString() : null));
  49. if (statusCode > 0 && statusCode < HttpStatus.BAD_REQUEST.value()) {
  50. log.info(marker, "request succeeded");
  51. } else {
  52. log.warn(marker, "request failed");
  53. }
  54. }
  55. return new HttpResp(responseHeaders, responseBody);
  56. }
  57. }