restTemplate踩過的坑-spring clound


 

現在公司項目基本都從臃腫的項目轉換成微服務的方向轉換,因此也從中使用了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調用是直接報錯的。

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

/**
 * 
 * 功能描述:
 * 
 * @作者 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>

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM