現在公司項目基本都從臃腫的項目轉換成微服務的方向轉換,因此也從中使用了spring clound的一些組件,在此過程中就遇到了restTemplate的坑。
起初,是直接注入RestTemplate,后來則不斷的遇到錯誤日志無法請求,出現異常。
異常信息:
因此不適用默認的,直接重新自定義,則引用了原有的注入修改一下。
/**
*
* 功能描述:
*
* @作者 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的坑就這么解決了。
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(null, new 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(3, true)); // 重試次數
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>
