一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - springboot restTemplate連接池整合方式

springboot restTemplate連接池整合方式

2022-02-26 00:36程猿薇蔦 Java教程

這篇文章主要介紹了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://www.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<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.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<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

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 91短视频社区在线观看 | 成人青青草 | 国产成人免费片在线观看 | 亚洲午夜精品久久久久久抢 | 四虎欧美| 国产激情一区二区三区成人91 | 白丝捆绑vk| 久久久久综合 | 精品国产一区二区在线观看 | 欧美肥bb| 69短视频| 日本护士xxxx视频 | 亚洲欧美久久久久久久久久爽网站 | 亚洲gogo人体大胆西西安徽 | 国产亚洲人成网站天堂岛 | 激情另类国内一区二区视频 | 激情乱文 | 亚洲天堂视频在线播放 | 精品国产视频 | 日本在线观看www | 男人和女人日比 | 无码国产成人777爽死 | 久久国产主播福利在线 | 精品免费国产一区二区三区 | 亚洲国产精品热久久 | 91av免费在线观看 | 日韩欧美一区二区三区四区 | 高h短篇校园1v1 | 夫妇交换小说全文阅读 | eee在线播放成人免费 | nxgx欧美| 日本道三区播放区 | videos变态极端| 国产精品1024永久免费视频 | 色橹橹 | 五月最新商场女厕所高跟嘘嘘 | 农村妇女野外性生话免费视频 | 寡妇快点好大好爽视频 | 午夜宅男影院 | 亚洲狠狠婷婷综合久久久久网站 | 亚洲国产精品婷婷久久久久 |