springboot restTemplate连接池整合方式

网友投稿 2253 2022-11-29

springboot restTemplate连接池整合方式

springboot restTemplate连接池整合方式

目录springboot restTemplate连接池整合restTemplate引入apache httpclientRestTemplate配置类RestTemplate连接池配置参数测试带连接池的RestTemplateRestTemplate 配置http连接池

springboot restTemplate连接池整合

restTemplate

使用http连接池能够减少连接建立与释放的时间,提升http请求的性能。如果客户端每次请求都要和服务端建立新的连接,即三次握手将会非常耗时。本文介绍如何在Springboot中集成http连接池;基于restTemplate+httpclient实现。

引入apache httpclient

org.apache.httpcomponents

httpclient

4.5.6

RestTemplate配置类

import org.apache.http.client.HttpClient;

import org.apache.http.impl.client.HttpClientBuilder;

import org.springframework.boot.context.properties.ConfigurationProperties;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.http.client.ClientHttpRequestFactory;

import org.springframework.http.client.HttpComponentsClientHtthttp://pRequestFactory;

import org.springframework.http.client.SimpleClientHttpRequestFactory;

import org.springframework.http.converter.HttpMessageConverter;

import org.springframework.http.converter.StringHttpMessageConverter;

import org.springframework.web.client.RestTemplate;

import java.nio.charset.Charset;

import java.util.List;

import java.util.concurrent.TimeUnit;

/**

* 实际开发中要避免每次http请求都实例化httpclient

* restTemplate默认会复用连接,保证restTemplate单例即可

* 参考资料:

* https://cnblogs.com/xrq730/p/10963689.html

* https://halfrost.com/advance_tcp/

*/

@Configuration

public class RestTemplateConfig {

@Bean

RestTemplate restTemplate(ClientHttpRequestFactory clientHttpRequestFactory) {

RestTemplate restTemplate = new RestTemplate(clientHttpRequestFactory);

List> messageConverters = restTemplate.getMessageConverters();

for (HttpMessageConverter c : messageConverters) {

if (c instanceof StringHttpMessageConverter) {

((StringHttpMessageConverter) c).setDefaultCharset(Charset.forName("utf-8"));

}

}

return restTemplate;

}

@Bean

@ConfigurationProperties(prefix = "spring.resttemplate")

HttpClientProperties httpClientProperties() {

return new HttpClientProperties();

}

@Bean

ClientHttpRequestFactory clientHttpRequestFactory(HttpClientProperties httpClientProperties) {

//如果不使用HttpClient的连接池,则使用restTemplate默认的SimpleClientHttpRequestFactory,底层基于HttpURLConnection

if (!httpClientProperties.isUseHttpClientPool()) {

SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();

factory.setConnectTimeout(httpClientProperties.getConnectTimeout());

factory.setReadTimeout(httpClientProperties.getReadTimeout());

return factory;

}

//HttpClient4.3及以上版本不手动设置HttpClientConnectionManager,默认就会使用连接池PoolingHttpClientConnectionManager

HttpClient httpClient = HttpClientBuilder.create().setMaxConnTotal(httpClientProperties.getMaxTotalConnect())

.setMaxConnPerRoute(httpClientProperties.getMaxConnectPerRoute()).evictExpiredConnections()

.evictIdleConnections(5000, TimeUnit.MILLISECONDS).build();

HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);

factory.setConnectTimeout(httpClientProperties.getConnectTimeout());

factory.setReadTimeout(httpClientProperties.getReadTimeout());

factory.setConnectionRequestTimeout(httpClientProperties.getConnectionRequestTimeout());

return factory;

}

}

RestTemplate连接池配置参数

public class HttpClientProperties {

/**

* 是否使用httpclient连接池

*/

private boolean useHttpClientPool = false;

/**

* 从连接池中获得一个connection的超时时间

*/

private int connectionRequestTimeout = 3000;

/**

* 建立连接超时时间

*/

private int connectTimeout = 3000;

/**

* 建立连接后读取返回数据的超时时间

*/

private int readTimeout = 5000;

/**

* 连接池的最大连接数,0代表不限

*/

private int maxTotalConnect = 128;

/**

* 每个路由的最大连接数

*/

private int maxConnectPerRoute = 32;

public int getConnectionRequestTimeout() {

return connectionRequestTimeout;

}

public void setConnectionRequestTimeout(int connectionRequestTimeout) {

this.connectionRequestTimeout = connectionRequestTimeout;

}

public int getConnectTimeout() {

return connectTimeout;

}

public void setConnectTimeout(int connectTimeout) {

this.connectTimeout = connectTimeout;

}

public int getReadTimeout() {

return readTimeout;

}

public void setReadTimeout(int readTimeout) {

this.readTimeout = readTimeout;

}

public ihttp://nt getMaxTotalConnect() {

return maxTotalConnect;

}

public void setMaxTotalConnect(int maxTotalConnect) {

this.maxTotalConnect = maxTotalConnect;

}

public int getMaxConnectPerRoute() {

return maxConnectPerRoute;

}

public void setMaxConnectPhttp://erRoute(int maxConnectPerRoute) {

this.maxConnectPerRoute = maxConnectPerRoute;

}

public boolean isUseHttpClientPool() {

return useHttpClientPool;

}

public void setUseHttpClientPool(boolean useHttpClientPool) {

this.useHttpClientPool = useHttpClientPool;

}

}

application.properties

spring.resttemplate.connectionRequestTimeout=3000

spring.resttemplate.connectTimeout=3000

spring.resttemplate.readTimeout=10000

spring.resttemplate.maxTotalConnect=256

spring.resttemplate.maxConnectPerRoute=128

spring.resttemplate.useHttpClientPool=true

测试带连接池的RestTemplate

import com.alibaba.fastjson.JSON;

import org.junit.Test;

import org.junit.runner.RunWith;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.boot.test.context.SpringBootTest;

import org.springframework.http.HttpEntity;

import org.springframework.http.HttpHeaders;

import org.springframework.http.HttpMethod;

import org.springframework.http.ResponseEntity;

import org.springframework.test.context.junit4.SpringRunner;

import org.springframework.web.client.RestTemplate;

import org.springframework.web.util.UriComponentsBuilder;

import java.util.Arrays;

import java.util.List;

import java.util.concurrent.ThreadLocalRandom;

@RunWith(SpringRunner.class)

@SpringBootTest

public class RestTemplateTest {

/**

* 免费查询号码归属地接口

*/

public String testUrl = "https://tcc.taobao.com/cc/json/mobile_tel_segment.htm";

@Autowired

RestTemplate restTemplate;

@Test

public void testRest() {

HttpHeaders headers = new HttpHeaders();

headers.set("Accept", "application/json");

HttpEntity entity = new HttpEntity(headers);

long start = System.currentTimeMillis();

for (int i = 0; i < 1000; i++) {

String tel = getRandomTel();

UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(testUrl).queryParam("tel", tel);

System.out.println("发送请求:" + builder.build().encode().toUri());

long startInner = System.currentTimeMillis();

ResponseEntity getDistrictRes = restTemplate.exchange(builder.build().encode().toUri(), HttpMethod.GET, entity, String.class);

long endInner = System.currentTimeMillis();

System.out.print("costPerRequest:" + (endInner - startInner) + ",i=" + i + "," + Thread.currentThread().getName());

String resJson = getDistrictRes.getBody().split("=")[1];

String carrier = (String) JSON.parseObject(resJson).get("carrier");

System.out.println("," + tel + ",归属地:" + carrier);

}

long end = System.currentTimeMillis();

System.out.println("costTotal:" + (end - start));

}

private String getRandomTel() {

List telList = Arrays.asList("18120168516", "15952044278", "15537788259", "18751872329", "13913329187");

int index = ThreadLocalRandom.current().nextInt(telList.size());

return telList.get(index);

}

}

测试比较发现,如果不设置ClientHttpRequestFactory,resttemplate默认会使用SimpleClientHttpRequestFactory,底层基于HttpURLConnection;这种方式和手动设置带连接池的httpComponentsClientHttpRequestFactory性能差别不大,基于httpclient的连接池性能稍有优势,不是太明显。

不管是使用restTemplate默认的SimpleClientHttpRequestFactory还是使用httpclient提供的HttpComponentsClientHttpRequestFactory,都会进行连接复用,即只有第一次请求耗时较高,后面的请求都复用连接。

使用httpclient可以设置evictExpiredConnections、evictIdleConnections进行定时清理过期、闲置连接。底层是开启了一个线程去执行清理任务,因此注意不能多次实例化httpclient相关的实例,会导致不断创建线程。

注意事项

实际开发中要避免每次http请求都实例化httpclient

restTemplate默认会复用连接,保证restTemplate单例即

RestTemplate 配置http连接池

import java.nio.charset.Charset;

import java.util.Iterator;

import java.util.List;

import org.apache.http.client.HttpClient;

import org.apache.http.conn.HttpClientConnectionManager;

import org.apache.http.impl.client.HttpClientBuilder;

import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;

import org.springframework.boot.web.client.RestTemplateBuilder;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.http.client.ClientHttpRequestFactory;

import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;

import org.springframework.http.converter.HttpMessageConverter;

import org.springframework.http.converter.StringHttpMessageConverter;

import org.springframework.web.client.RestTemplate;

@Configuration

public class RestTemplateUtil{

@Bean

public RestTemplate restTemplate(RestTemplateBuilder builder) {

RestTemplate restTemplate = builder.build();

restTemplate.setRequestFactory(clientHttpRequestFactory());

// 使用 utf-8 编码集的 conver 替换默认的 conver(默认的 string conver 的编码集为"ISO-8859-1")

List> messageConverters = restTemplate.getMessageConverters();

Iterator> iterator = messageConverters.iterator();

while (iterator.hasNext()) {

HttpMessageConverter> converter = iterator.next();

if (converter instanceof StringHttpMessageConverter) {

iterator.remove();

}

}

messageConverters.add(new StringHttpMessageConverter(Charset.forName("UTF-8")));

return restTemplate;

}

@Bean

public HttpClientConnectionManager poolingConnectionManager() {

PoolingHttpClientConnectionManager poolingConnectionManager = new PoolingHttpClientConnectionManager();

poolingConnectionManager.setMaxTotal(1000); // 连接池最大连接数

poolingConnectionManager.setDefaultMaxPerRoute(100); // 每个主机的并发

return poolingConnectionManager;

}

@Bean

public HttpClientBuilder httpClientBuilder() {

HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();

//设置HTTP连接管理

httpClientBuilder.setConnectionManager(poolingConnectionManager());

return httpClientBuilder;

}

@Bean

public ClientHttpRequestFactory clientHttpRequestFactory() {

HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory();

clientHttpRequestFactory.setHttpClient(httpClientBuilder().build());

clientHttpRequestFactory.setConnectTimeout(6000); // 连接超时,毫秒

clientHttpRequestFactory.setReadTimeout(6000); // 读写超时,毫秒

return clientHttpRequestFactory;

}

}

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:卫星导航系统-第12讲-差分定位方法-2
下一篇:卫星导航系统-第16讲-船用惯性导航系统与卫星导航系统的组合-2
相关文章

 发表评论

暂时没有评论,来抢沙发吧~