好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

springboot restTemplate连接池整合方式

springboot restTemplate连接池整合

restTemplate

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

引入apache httpclient

?

1

2

3

4

5

< dependency >

     < groupId >org.apache.httpcomponents</ groupId >

     < artifactId >httpclient</ artifactId >

     < version >4.5.6</ version >

</ dependency >

RestTemplate配置类

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

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.HttpComponentsClientHttpRequestFactory;

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://HdhCmsTestcnblogs测试数据/xrq730/p/10963689.html

  * https://halfrost测试数据/advance_tcp/

  */

@Configuration

public class RestTemplateConfig {

     @Bean

     RestTemplate restTemplate(ClientHttpRequestFactory clientHttpRequestFactory) {

         RestTemplate restTemplate = new RestTemplate(clientHttpRequestFactory);

         List<HttpMessageConverter<?>> 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连接池配置参数

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

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 int getMaxTotalConnect() {

         return maxTotalConnect;

     }

     public void setMaxTotalConnect( int maxTotalConnect) {

         this .maxTotalConnect = maxTotalConnect;

     }

     public int getMaxConnectPerRoute() {

         return maxConnectPerRoute;

     }

     public void setMaxConnectPerRoute( int maxConnectPerRoute) {

         this .maxConnectPerRoute = maxConnectPerRoute;

     }

     public boolean isUseHttpClientPool() {

         return useHttpClientPool;

     }

     public void setUseHttpClientPool( boolean useHttpClientPool) {

         this .useHttpClientPool = useHttpClientPool;

     }

}

application.properties

?

1

2

3

4

5

6

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

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

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测试数据/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<String> 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<String> 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连接池

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

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<HttpMessageConverter<?>> messageConverters = restTemplate.getMessageConverters();

         Iterator<HttpMessageConverter<?>> 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;

     }

}

以上为个人经验,希望能给大家一个参考,也希望大家多多支持。

原文链接:https://bigbird.blog.csdn.net/article/details/106861972

查看更多关于springboot restTemplate连接池整合方式的详细内容...

  阅读:25次