restTemplate踩過的坑- 內部服務調用重試次數偶發堵塞

現在公司項目基本都從臃腫的項目轉換成微服務的方向轉換,因此也從中使用了spring clound的一些組件,在此過程中就遇到了restTemplate的坑。

起初,是直接注入RestTemplate,後來則不斷的遇到錯誤日誌無法請求,出現異常。

異常信息:

org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.IllegalStateException: No instances available for IP

意思是說url必須是服務的名稱,猜測應該是涉及到eureka,直接用ip跟url調用是直接報錯的。

因此不適用默認的,直接重新自定義,則引用了原有的注入修改一下。

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

/**

 *

 * 功能描述:

 *

 * @作者 jimw 創建時間:2018-04

 *

 */

@Configuration

public class RestTemplateConfig {

 

    public RestTemplate restTemplate() {

        return new RestTemplate(getClientHttpRequestFactory());

    }

 

    /**

     * 配置HttpClient超時時間

     *

     * @return

     */

    private ClientHttpRequestFactory getClientHttpRequestFactory() {

        RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(HTTP_SOCKET_TIMEOUT)

                .setConnectTimeout(HTTP_CONNECT_TIMEOUT).build();

        CloseableHttpClient client = HttpClientBuilder.create().setDefaultRequestConfig(requestConfig).build();

        return new HttpComponentsClientHttpRequestFactory(client);

    }

 

    /** http請求socket連接超時時間,毫秒爲單位 */

    public static final int HTTP_SOCKET_TIMEOUT = 15000;

 

    /** http請求連接超時時間,毫秒爲單位 */

    public static final int HTTP_CONNECT_TIMEOUT = 15000;

}

  

當配置了這個之後,服務正常了。觀察了一段時間,發現在併發的高峯期,開多幾個服務器負載,也會存在服務出現請求非常慢,導致接口出現阻塞的情況出現,經過分析

1、出現阻塞的原因是因爲高峯併發的時候,出現請求的鏈接數很大

因此找了源碼,發現是因爲restTemplate的默認配置值小於請求的鏈接數,而且路由併發也是默認爲5的,因爲微服務之間的邏輯處理中也有一定的時間。出現大規模阻塞的坑就這麼踩到了。 

 

對代碼改造一下,配置最大鏈接數,路由併發數,這個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

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

package com.jingbei.guess.config.web;

 

import java.security.KeyManagementException;

import java.security.KeyStoreException;

import java.security.NoSuchAlgorithmException;

import java.security.cert.CertificateException;

import java.security.cert.X509Certificate;

 

import javax.net.ssl.HostnameVerifier;

import javax.net.ssl.SSLContext;

 

import org.apache.http.client.HttpClient;

import org.apache.http.config.Registry;

import org.apache.http.config.RegistryBuilder;

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.conn.ssl.SSLContextBuilder;

import org.apache.http.conn.ssl.TrustStrategy;

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

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

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

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;

import org.springframework.web.client.DefaultResponseErrorHandler;

import org.springframework.web.client.RestTemplate;

 

import lombok.extern.slf4j.Slf4j;

 

/**

 *

 * 功能描述:

 *

 * @作者 jimw 創建時間: 2018-04

 *

 */

@Slf4j

@Configuration

public class RestTemplateConfig {

    @Bean

    public RestTemplate restTemplate() {

        RestTemplate restTemplate = new RestTemplate();

        restTemplate.setRequestFactory(clientHttpRequestFactory());

        restTemplate.setErrorHandler(new DefaultResponseErrorHandler());

        return restTemplate;

    }

 

    @Bean

    public HttpComponentsClientHttpRequestFactory clientHttpRequestFactory() {

        try {

            HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();

            SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(nullnew TrustStrategy() {

                public boolean isTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {

                    return true;

                }

            }).build();

            httpClientBuilder.setSSLContext(sslContext);

            HostnameVerifier hostnameVerifier = NoopHostnameVerifier.INSTANCE;

            SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext,

                    hostnameVerifier);

            Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()

                    .register("http", PlainConnectionSocketFactory.getSocketFactory())

                    .register("https", sslConnectionSocketFactory).build();// 註冊http和https請求

            // 開始設置連接池

            PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager(

                    socketFactoryRegistry);

            poolingHttpClientConnectionManager.setMaxTotal(2700); // 最大連接數2700

            poolingHttpClientConnectionManager.setDefaultMaxPerRoute(100); // 同路由併發數100

            httpClientBuilder.setConnectionManager(poolingHttpClientConnectionManager);

            httpClientBuilder.setRetryHandler(new DefaultHttpRequestRetryHandler(3true)); // 重試次數

            HttpClient httpClient = httpClientBuilder.build();

            HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(

                    httpClient); // httpClient連接配置

            clientHttpRequestFactory.setConnectTimeout(20000); // 連接超時

            clientHttpRequestFactory.setReadTimeout(30000); // 數據讀取超時時間

            clientHttpRequestFactory.setConnectionRequestTimeout(20000); // 連接不夠用的等待時間

            return clientHttpRequestFactory;

        catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException e) {

            log.error("初始化HTTP連接池出錯", e);

        }

        return null;

    }

 

}

  在對應的插件中配置即可

依賴包:

<dependency>

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

<artifactId>httpclient</artifactId>

<version>4.5.3</version>

</dependency>

<dependency>

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

<artifactId>httpcore</artifactId>

<version>4.4.9</version>

</dependency>

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章