本站消息

站长简介/公众号

  出租广告位,需要合作请联系站长


+关注
已关注

分类  

暂无分类

标签  

暂无标签

日期归档  

暂无数据

SpringBoot RestTemplate 整合 HttpClient 连接池 配置长连接

发布于2021-04-02 06:36     阅读(3987)     评论(0)     点赞(11)     收藏(5)


一,建立配置属性文件

  1. spring:
  2. http-client:
  3. pool:
  4. #连接池的最大连接数,0代表不限;如果取0,需要考虑连接泄露导致系统崩溃的后果
  5. maxTotalConnect: 1000
  6. #每个路由的最大连接数,如果只调用一个地址,可以将其设置为最大连接数
  7. maxConnectPerRoute: 200
  8. # 指客户端和服务器建立连接的超时时间,ms , 最大约21秒,因为内部tcp在进行三次握手建立连接时,默认tcp超时时间是20秒
  9. connectTimeout: 3000
  10. # 指客户端从服务器读取数据包的间隔超时时间,不是总读取时间,也就是socket timeout,ms
  11. readTimeout: 5000
  12. # 从连接池获取连接的timeout,不宜过大,ms
  13. connectionRequestTimout: 200
  14. # 重试次数
  15. retryTimes: 3
  16. charset: UTF-8
  17. # 长连接保持时间 单位s,不宜过长
  18. keepAliveTime: 10
  19. keepAliveTargetHost:
  20. www.baidu.com: 5

二,创建配置属性类

  1. package com.aishua.zdjfzxq.config;
  2. import org.springframework.boot.context.properties.ConfigurationProperties;
  3. import org.springframework.stereotype.Component;
  4. import java.util.Map;
  5. /**
  6. * @Author: zh
  7. * @Date:
  8. * @Description:
  9. */
  10. @Component
  11. @ConfigurationProperties(prefix = "spring.http-client.pool")
  12. public class HttpClientPoolConfig {
  13. /**
  14. * java配置的优先级低于yml配置;如果yml配置不存在,会采用java配置
  15. */
  16. /**
  17. * 连接池的最大连接数
  18. */
  19. private int maxTotalConnect ;
  20. /**
  21. * 同路由的并发数
  22. */
  23. private int maxConnectPerRoute ;
  24. /**
  25. * 客户端和服务器建立连接超时,默认2s
  26. */
  27. private int connectTimeout = 2 * 1000;
  28. /**
  29. * 指客户端从服务器读取数据包的间隔超时时间,不是总读取时间,默认30s
  30. */
  31. private int readTimeout = 30 * 1000;
  32. private String charset = "UTF-8";
  33. /**
  34. * 重试次数,默认2次
  35. */
  36. private int retryTimes = 2;
  37. /**
  38. * 从连接池获取连接的超时时间,不宜过长,单位ms
  39. */
  40. private int connectionRequestTimout = 200;
  41. /**
  42. * 针对不同的地址,特别设置不同的长连接保持时间
  43. */
  44. private Map<String,Integer> keepAliveTargetHost;
  45. /**
  46. * 针对不同的地址,特别设置不同的长连接保持时间,单位 s
  47. */
  48. private int keepAliveTime = 60;
  49. public int getMaxTotalConnect() {
  50. return maxTotalConnect;
  51. }
  52. public void setMaxTotalConnect(int maxTotalConnect) {
  53. this.maxTotalConnect = maxTotalConnect;
  54. }
  55. public int getMaxConnectPerRoute() {
  56. return maxConnectPerRoute;
  57. }
  58. public void setMaxConnectPerRoute(int maxConnectPerRoute) {
  59. this.maxConnectPerRoute = maxConnectPerRoute;
  60. }
  61. public int getConnectTimeout() {
  62. return connectTimeout;
  63. }
  64. public void setConnectTimeout(int connectTimeout) {
  65. this.connectTimeout = connectTimeout;
  66. }
  67. public int getReadTimeout() {
  68. return readTimeout;
  69. }
  70. public void setReadTimeout(int readTimeout) {
  71. this.readTimeout = readTimeout;
  72. }
  73. public String getCharset() {
  74. return charset;
  75. }
  76. public void setCharset(String charset) {
  77. this.charset = charset;
  78. }
  79. public int getRetryTimes() {
  80. return retryTimes;
  81. }
  82. public void setRetryTimes(int retryTimes) {
  83. this.retryTimes = retryTimes;
  84. }
  85. public int getConnectionRequestTimout() {
  86. return connectionRequestTimout;
  87. }
  88. public void setConnectionRequestTimout(int connectionRequestTimout) {
  89. this.connectionRequestTimout = connectionRequestTimout;
  90. }
  91. public Map<String, Integer> getKeepAliveTargetHost() {
  92. return keepAliveTargetHost;
  93. }
  94. public void setKeepAliveTargetHost(Map<String, Integer> keepAliveTargetHost) {
  95. this.keepAliveTargetHost = keepAliveTargetHost;
  96. }
  97. public int getKeepAliveTime() {
  98. return keepAliveTime;
  99. }
  100. public void setKeepAliveTime(int keepAliveTime) {
  101. this.keepAliveTime = keepAliveTime;
  102. }
  103. }

三,创建 连接池

  1. package com.aishua.zdjfzxq.config;
  2. import com.alibaba.fastjson.JSON;
  3. import org.apache.http.Header;
  4. import org.apache.http.HeaderElement;
  5. import org.apache.http.HeaderElementIterator;
  6. import org.apache.http.HttpHost;
  7. import org.apache.http.client.HttpClient;
  8. import org.apache.http.client.protocol.HttpClientContext;
  9. import org.apache.http.config.Registry;
  10. import org.apache.http.config.RegistryBuilder;
  11. import org.apache.http.conn.ConnectionKeepAliveStrategy;
  12. import org.apache.http.conn.socket.ConnectionSocketFactory;
  13. import org.apache.http.conn.socket.PlainConnectionSocketFactory;
  14. import org.apache.http.conn.ssl.NoopHostnameVerifier;
  15. import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
  16. import org.apache.http.impl.client.CloseableHttpClient;
  17. import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
  18. import org.apache.http.impl.client.HttpClientBuilder;
  19. import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
  20. import org.apache.http.message.BasicHeader;
  21. import org.apache.http.message.BasicHeaderElementIterator;
  22. import org.apache.http.protocol.HTTP;
  23. import org.apache.http.ssl.SSLContextBuilder;
  24. import org.slf4j.Logger;
  25. import org.slf4j.LoggerFactory;
  26. import org.springframework.beans.factory.annotation.Autowired;
  27. import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
  28. import org.springframework.context.annotation.Bean;
  29. import org.springframework.context.annotation.Configuration;
  30. import org.springframework.http.client.ClientHttpRequestFactory;
  31. import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
  32. import org.springframework.http.converter.HttpMessageConverter;
  33. import org.springframework.http.converter.StringHttpMessageConverter;
  34. import org.springframework.stereotype.Component;
  35. import org.springframework.web.client.DefaultResponseErrorHandler;
  36. import org.springframework.web.client.RestTemplate;
  37. import javax.net.ssl.HostnameVerifier;
  38. import javax.net.ssl.SSLContext;
  39. import java.nio.charset.Charset;
  40. import java.security.KeyManagementException;
  41. import java.security.KeyStoreException;
  42. import java.security.NoSuchAlgorithmException;
  43. import java.util.*;
  44. /**
  45. * @Author: zh
  46. * @Date:
  47. * @Description:
  48. */
  49. @Configuration
  50. @Component
  51. @ConditionalOnClass(value = {RestTemplate.class, CloseableHttpClient.class})
  52. public class HttpClientConfig {
  53. private final Logger log = LoggerFactory.getLogger(this.getClass());
  54. @Autowired
  55. private HttpClientPoolConfig httpClientPoolConfig;
  56. @Bean
  57. public RestTemplate restTemplate() {
  58. return new RestTemplate();
  59. }
  60. /**
  61. * 创建HTTP客户端工厂
  62. */
  63. @Bean(name = "clientHttpRequestFactory")
  64. public ClientHttpRequestFactory clientHttpRequestFactory() {
  65. /**
  66. * maxTotalConnection 和 maxConnectionPerRoute 必须要配
  67. */
  68. if (httpClientPoolConfig.getMaxTotalConnect() <= 0) {
  69. throw new IllegalArgumentException("invalid maxTotalConnection: " + httpClientPoolConfig.getMaxTotalConnect());
  70. }
  71. if (httpClientPoolConfig.getMaxConnectPerRoute() <= 0) {
  72. throw new IllegalArgumentException("invalid maxConnectionPerRoute: " + httpClientPoolConfig.getMaxConnectPerRoute());
  73. }
  74. HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(httpClient());
  75. // 连接超时
  76. clientHttpRequestFactory.setConnectTimeout(httpClientPoolConfig.getConnectTimeout());
  77. // 数据读取超时时间,即SocketTimeout
  78. clientHttpRequestFactory.setReadTimeout(httpClientPoolConfig.getReadTimeout());
  79. // 从连接池获取请求连接的超时时间,不宜过长,必须设置,比如连接不够用时,时间过长将是灾难性的
  80. clientHttpRequestFactory.setConnectionRequestTimeout(httpClientPoolConfig.getConnectionRequestTimout());
  81. return clientHttpRequestFactory;
  82. }
  83. /**
  84. * 初始化RestTemplate,并加入spring的Bean工厂,由spring统一管理
  85. */
  86. @Bean(name = "httpClientTemplate")
  87. public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
  88. return createRestTemplate(factory);
  89. }
  90. /**
  91. * 配置httpClient
  92. *
  93. * @return
  94. */
  95. @Bean
  96. public HttpClient httpClient() {
  97. HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
  98. try {
  99. //设置信任ssl访问
  100. SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, (arg0, arg1) -> true).build();
  101. httpClientBuilder.setSSLContext(sslContext);
  102. HostnameVerifier hostnameVerifier = NoopHostnameVerifier.INSTANCE;
  103. SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext, hostnameVerifier);
  104. Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
  105. // 注册http和https请求
  106. .register("http", PlainConnectionSocketFactory.getSocketFactory())
  107. .register("https", sslConnectionSocketFactory).build();
  108. //使用Httpclient连接池的方式配置(推荐),同时支持netty,okHttp以及其他http框架
  109. PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
  110. // 最大连接数
  111. poolingHttpClientConnectionManager.setMaxTotal(httpClientPoolConfig.getMaxTotalConnect());
  112. // 同路由并发数
  113. poolingHttpClientConnectionManager.setDefaultMaxPerRoute(httpClientPoolConfig.getMaxConnectPerRoute());
  114. //配置连接池
  115. httpClientBuilder.setConnectionManager(poolingHttpClientConnectionManager);
  116. // 重试次数
  117. httpClientBuilder.setRetryHandler(new DefaultHttpRequestRetryHandler(httpClientPoolConfig.getRetryTimes(), true));
  118. //设置默认请求头
  119. List<Header> headers = getDefaultHeaders();
  120. httpClientBuilder.setDefaultHeaders(headers);
  121. //设置长连接保持策略
  122. httpClientBuilder.setKeepAliveStrategy(connectionKeepAliveStrategy());
  123. return httpClientBuilder.build();
  124. } catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException e) {
  125. log.error("初始化HTTP连接池出错", e);
  126. }
  127. return null;
  128. }
  129. /**
  130. * 配置长连接保持策略
  131. * @return
  132. */
  133. public ConnectionKeepAliveStrategy connectionKeepAliveStrategy(){
  134. return (response, context) -> {
  135. // Honor 'keep-alive' header
  136. HeaderElementIterator it = new BasicHeaderElementIterator(
  137. response.headerIterator(HTTP.CONN_KEEP_ALIVE));
  138. while (it.hasNext()) {
  139. HeaderElement he = it.nextElement();
  140. String param = he.getName();
  141. String value = he.getValue();
  142. if (value != null && "timeout".equalsIgnoreCase(param)) {
  143. try {
  144. return Long.parseLong(value) * 1000;
  145. } catch(NumberFormatException ignore) {
  146. log.error("解析长连接过期时间异常",ignore);
  147. }
  148. }
  149. }
  150. HttpHost target = (HttpHost) context.getAttribute(
  151. HttpClientContext.HTTP_TARGET_HOST);
  152. //如果请求目标地址,单独配置了长连接保持时间,使用该配置
  153. Optional<Map.Entry<String, Integer>> any = Optional.ofNullable(httpClientPoolConfig.getKeepAliveTargetHost()).orElseGet(HashMap::new)
  154. .entrySet().stream().filter(
  155. e -> e.getKey().equalsIgnoreCase(target.getHostName())).findAny();
  156. //否则使用默认长连接保持时间
  157. return any.map(en -> en.getValue() * 1000L).orElse(httpClientPoolConfig.getKeepAliveTime() * 1000L);
  158. };
  159. }
  160. /**
  161. * 设置请求头
  162. *
  163. * @return
  164. */
  165. private List<Header> getDefaultHeaders() {
  166. List<Header> headers = new ArrayList<>();
  167. headers.add(new BasicHeader("User-Agent",
  168. "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.16 Safari/537.36"));
  169. headers.add(new BasicHeader("Accept-Encoding", "gzip,deflate"));
  170. headers.add(new BasicHeader("Accept-Language", "zh-CN"));
  171. headers.add(new BasicHeader("Connection", "Keep-Alive"));
  172. return headers;
  173. }
  174. private RestTemplate createRestTemplate(ClientHttpRequestFactory factory) {
  175. RestTemplate restTemplate = new RestTemplate(factory);
  176. //我们采用RestTemplate内部的MessageConverter
  177. //重新设置StringHttpMessageConverter字符集,解决中文乱码问题
  178. modifyDefaultCharset(restTemplate);
  179. //设置错误处理器
  180. restTemplate.setErrorHandler(new DefaultResponseErrorHandler());
  181. return restTemplate;
  182. }
  183. /**
  184. * 修改默认的字符集类型为utf-8
  185. *
  186. * @param restTemplate
  187. */
  188. private void modifyDefaultCharset(RestTemplate restTemplate) {
  189. List<HttpMessageConverter<?>> converterList = restTemplate.getMessageConverters();
  190. HttpMessageConverter<?> converterTarget = null;
  191. for (HttpMessageConverter<?> item : converterList) {
  192. if (StringHttpMessageConverter.class == item.getClass()) {
  193. converterTarget = item;
  194. break;
  195. }
  196. }
  197. if (null != converterTarget) {
  198. converterList.remove(converterTarget);
  199. }
  200. Charset defaultCharset = Charset.forName(httpClientPoolConfig.getCharset());
  201. converterList.add(1, new StringHttpMessageConverter(defaultCharset));
  202. }
  203. }

四、使用案例

  1. package com.aishua.zdjfzxq.service.impl;
  2. import com.aishua.zdjfzxq.entity.ZdjfBatchPlan;
  3. import com.aishua.zdjfzxq.framework.SingleThreadPullService;
  4. import org.springframework.beans.factory.annotation.Autowired;
  5. import org.springframework.stereotype.Service;
  6. import org.springframework.web.client.RestClientException;
  7. import org.springframework.web.client.RestTemplate;
  8. import javax.annotation.Resource;
  9. import java.util.Arrays;
  10. import java.util.List;
  11. @Service
  12. public class ZxqPullService implements SingleThreadPullService {
  13. @Resource
  14. RestTemplate restTemplate;
  15. @Resource
  16. private RestTemplate httpClientTemplate;
  17. @Override
  18. public List pullList(int size) {
  19. String host = "localhost";
  20. String port = "8080";
  21. String url = "http://" + host + ":" + port + "/FfqWorkController?name={1}";
  22. ZdjfBatchPlan[] list= new ZdjfBatchPlan[0];
  23. try {
  24. list = httpClientTemplate.getForObject("http://localhost:8080/FfqWorkController/obtain?num={1}",ZdjfBatchPlan[].class,size);
  25. } catch (RestClientException e) {
  26. e.printStackTrace();
  27. }
  28. return Arrays.asList(list);
  29. }
  30. }

 



所属网站分类: 技术文章 > 博客

作者:忽明忽灭

链接:http://www.javaheidong.com/blog/article/137151/581364ae5b7d556e707e/

来源:java黑洞网

任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任

11 0
收藏该文
已收藏

评论内容:(最多支持255个字符)