springboot中RestTemplate配置HttpClient连接池详解
作者:morris131
RestTemplate配置HttpClient连接池
在Java开发中,访问第三方HTTP协议的网络接口,通常使用的连接工具为JDK自带的HttpURLConnection、HttpClient(现在应该称之为HttpComponents)和OKHttp。
这些Http连接工具,使用起来都比较复杂,如果项目中使用的是Spring框架,可以使用Spring自带的RestTemplate来进行Http连接请求。
RestTemplate底层默认的连接方式是Java中的HttpURLConnection,可以使用ClientHttpRequestFactory来指定底层使用不同的HTTP连接方式。
RestTemplate中默认的连接方式
RestTemplate中默认使用的是SimpleClientHttpRequestFactory,我们这里手动创建SimpleClientHttpRequestFactory可以指定连接的超时时间,读数据的超时时间。
package com.morris.user.demo; import com.morris.user.entity.Order; import lombok.extern.slf4j.Slf4j; import org.springframework.http.client.SimpleClientHttpRequestFactory; import org.springframework.web.client.RestTemplate; /** * restTemplate+httpUrlConnection */ @Slf4j public class RestTemplateDemo1 { public static void main(String[] args) { SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory(); factory.setConnectTimeout(3000); factory.setReadTimeout(5000); RestTemplate restTemplate = new RestTemplate(factory); Order[] orders = restTemplate.getForObject("http://127.0.0.1:8020/order/findOrderByUserId?userId=", Order[].class, 1); log.info("orders :{}", orders); } }
SimpleClientHttpRequestFactory底层在创建请求的时候使用的就是HttpURLConnection。
org.springframework.http.client.SimpleClientHttpRequestFactory#createRequest
public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException { HttpURLConnection connection = openConnection(uri.toURL(), this.proxy); prepareConnection(connection, httpMethod.name()); if (this.bufferRequestBody) { return new SimpleBufferingClientHttpRequest(connection, this.outputStreaming); } else { return new SimpleStreamingClientHttpRequest(connection, this.chunkSize, this.outputStreaming); } }
RestTemplate与HttpClient的结合
只需要在构造RestTemplate实例时传入HttpComponentsClientHttpRequestFactory对象即可。
package com.morris.user.demo; import com.morris.user.entity.Order; import lombok.extern.slf4j.Slf4j; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.web.client.RestTemplate; /** * RestTemplate+HttpClient */ @Slf4j public class RestTemplateDemo2 { public static void main(String[] args) { HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(); RestTemplate restTemplate = new RestTemplate(factory); Order[] orders = restTemplate.getForObject("http://127.0.0.1:8020/order/findOrderByUserId?userId=", Order[].class, 1); log.info("orders :{}", orders); } }
HttpComponentsClientHttpRequestFactory底层在创建请求时使用了HttpClient。
org.springframework.http.client.HttpComponentsClientHttpRequestFactory#createRequest
public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException { HttpClient client = getHttpClient(); HttpUriRequest httpRequest = createHttpUriRequest(httpMethod, uri); postProcessHttpRequest(httpRequest); HttpContext context = createHttpContext(httpMethod, uri); if (context == null) { context = HttpClientContext.create(); } // Request configuration not set in the context if (context.getAttribute(HttpClientContext.REQUEST_CONFIG) == null) { // Use request configuration given by the user, when available RequestConfig config = null; if (httpRequest instanceof Configurable) { config = ((Configurable) httpRequest).getConfig(); } if (config == null) { config = createRequestConfig(client); } if (config != null) { context.setAttribute(HttpClientContext.REQUEST_CONFIG, config); } } if (this.bufferRequestBody) { return new HttpComponentsClientHttpRequest(client, httpRequest, context); } else { return new HttpComponentsStreamingClientHttpRequest(client, httpRequest, context); } }
RestTemplate与HttpClient的在生产环境使用的最佳实践
在构建HttpClient时,经常需要配置很多信息,例如RequestTimeout、ConnectTimeout、SocketTimeout、代理、是否允许重定向、连接池等信息。
在HttpClient,对这些参数进行配置需要使用到RequestConfig类的一个内部类Builder。
这里将这些常用的配置抽取出来放到配置文件中:
package com.morris.user.config; import lombok.extern.slf4j.Slf4j; import org.apache.http.HeaderElement; import org.apache.http.HeaderElementIterator; import org.apache.http.HttpHost; import org.apache.http.client.HttpClient; import org.apache.http.client.protocol.HttpClientContext; import org.apache.http.config.Registry; import org.apache.http.config.RegistryBuilder; import org.apache.http.conn.ConnectionKeepAliveStrategy; import org.apache.http.conn.socket.ConnectionSocketFactory; import org.apache.http.conn.socket.PlainConnectionSocketFactory; import org.apache.http.conn.ssl.NoopHostnameVerifier; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.DefaultHttpRequestRetryHandler; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.message.BasicHeaderElementIterator; import org.apache.http.protocol.HTTP; import org.apache.http.ssl.SSLContextBuilder; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.http.client.ClientHttpRequestFactory; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.web.client.RestTemplate; import javax.annotation.Resource; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLContext; import java.security.KeyManagementException; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.util.HashMap; import java.util.Map; import java.util.Optional; @EnableConfigurationProperties(HttpClientConfig.class) @ConditionalOnClass(RestTemplate.class) @Configuration @Slf4j public class RestTemplateAutoConfiguration { @Resource private HttpClientConfig httpClientConfig; @Bean @ConditionalOnClass(CloseableHttpClient.class) public RestTemplate httpClientRestTemplate(ClientHttpRequestFactory clientHttpRequestFactory){ return new RestTemplate(clientHttpRequestFactory); } @Bean @ConditionalOnClass(CloseableHttpClient.class) public ClientHttpRequestFactory clientHttpRequestFactory(HttpClient httpClient) { HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(); clientHttpRequestFactory.setHttpClient(httpClient); clientHttpRequestFactory.setConnectTimeout(httpClientConfig.getRequest().getConnectTimeout()); clientHttpRequestFactory.setReadTimeout(httpClientConfig.getRequest().getReadTimeout()); clientHttpRequestFactory.setConnectionRequestTimeout(httpClientConfig.getRequest().getConnectionRequestTimeout()); clientHttpRequestFactory.setBufferRequestBody(httpClientConfig.getRequest().isBufferRequestBody()); return clientHttpRequestFactory; } @Bean @Primary @ConditionalOnClass(CloseableHttpClient.class) public HttpClient httpClient() throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException { HttpClientBuilder httpClientBuilder = HttpClientBuilder.create(); try { // 设置信任SSL访问 SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, (arg0, arg1) -> true).build(); httpClientBuilder.setSSLContext(sslContext); // 任何主机都不会抛出SSLException异常 HostnameVerifier hostnameVerifier = NoopHostnameVerifier.INSTANCE; SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext, hostnameVerifier); Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create() // 注册HTTP和HTTPS请求 .register("http", PlainConnectionSocketFactory.getSocketFactory()) .register("https", sslConnectionSocketFactory).build(); // 使用Httpclient连接池的方式配置 PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry); poolingHttpClientConnectionManager.setMaxTotal(httpClientConfig.getPool().getMaxTotalConnect()); poolingHttpClientConnectionManager.setDefaultMaxPerRoute(httpClientConfig.getPool().getMaxConnectPerRoute()); poolingHttpClientConnectionManager.setValidateAfterInactivity(httpClientConfig.getPool().getValidateAfterInactivity()); httpClientBuilder.setConnectionManager(poolingHttpClientConnectionManager); httpClientBuilder.setRetryHandler(new DefaultHttpRequestRetryHandler(httpClientConfig.getPool().getRetryTimes(), true)); httpClientBuilder.setKeepAliveStrategy(connectionKeepAliveStrategy()); return httpClientBuilder.build(); } catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException e) { log.error("初始化HTTP连接池出错", e); throw e; } } /** * 配置长连接保持策略 * @return ConnectionKeepAliveStrategy */ public ConnectionKeepAliveStrategy connectionKeepAliveStrategy(){ return (response, context) -> { // Honor 'keep-alive' header HeaderElementIterator it = new BasicHeaderElementIterator( response.headerIterator(HTTP.CONN_KEEP_ALIVE)); while (it.hasNext()) { HeaderElement he = it.nextElement(); String param = he.getName(); String value = he.getValue(); if (value != null && "timeout".equalsIgnoreCase(param)) { try { return Long.parseLong(value) * 1000; } catch(NumberFormatException error) { log.error("解析长连接过期时间异常", error); } } } HttpHost target = (HttpHost) context.getAttribute(HttpClientContext.HTTP_TARGET_HOST); //如果请求目标地址,单独配置了长连接保持时间,使用该配置 Optional<Map.Entry<String, Integer>> any = Optional.ofNullable(httpClientConfig.getPool().getKeepAliveTargetHost()).orElseGet(HashMap::new) .entrySet().stream().filter( e -> e.getKey().equalsIgnoreCase(target.getHostName())).findAny(); //否则使用默认长连接保持时间 return any.map(en -> en.getValue() * 1000L).orElse(httpClientConfig.getPool().getKeepAliveTime() * 1000L); }; } }
到此这篇关于springboot中RestTemplate配置HttpClient连接池详解的文章就介绍到这了,更多相关RestTemplate配置HttpClient连接池内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
您可能感兴趣的文章:
- SpringBoot实现对Http接口进行监控的代码
- springboot配置请求超时时间(Http会话和接口访问)
- springboot 实现Http接口加签、验签操作方法
- SpringBoot 项目使用hutool 工具进行 http 接口调用的处理方法
- SpringBoot 接口开发教程(httpclient客户端)
- Springboot单体架构http请求转换https请求来支持微信小程序调用接口
- SpringBoot使用RestTemplate实现HTTP请求详解
- 基于springboot的RestTemplate、okhttp和HttpClient对比分析
- SpringBoot如何使用Template请求http接口