RestTemplate 連接池:記一次壓測調(diào)優(yōu)
[ 用大白話講解復(fù)雜的技術(shù) ]
最近工作上有些調(diào)整,進(jìn)入了另一個項目組,本周在給一個接口做壓力測試的過程中,發(fā)現(xiàn) TPS 波動比較大:單臺服務(wù)器的 TPS 在 1000 到 200 之間波動,發(fā)現(xiàn)問題后我們做了這幾件事兒逐一排問題。
1. 排除其他操作對壓測的影響
觀察數(shù)據(jù)的時間戳,發(fā)現(xiàn)在壓測過程中有頻發(fā)的數(shù)據(jù)寫入,先暫停跑批操作,再次壓測波動依然存在;
2. 代碼檢查,降低數(shù)據(jù)庫查詢次數(shù)
做代碼檢查,這個功能需要查詢六次數(shù)據(jù)庫、調(diào)用一次接口才能獲得所有數(shù)據(jù),經(jīng)過優(yōu)化后將查詢數(shù)據(jù)庫的次數(shù)降低為三次,再次壓測后 TPS 有所提升,但是波動的情況還存在;
3. 分階段壓測,縮小問題范圍
因為該接口的邏輯是本地邏輯 + 遠(yuǎn)程調(diào)用,于是先做了擋板單壓本地邏輯,再單獨壓遠(yuǎn)程接口,兩次壓測不存在波動的問題,所以基本上可以確定是調(diào)用接口的過程中存在一些問題;
4. 排除 GC 問題
調(diào)用接口的過程中創(chuàng)建了太多的 Java 對象,可能會引發(fā)頻發(fā)的 GC 操作;但是通過對 JVM 的觀察排除了這個問題;
5. 初步確認(rèn)問題
最后將問題的范圍縮小,懷疑是 RestTemplate 的使用問題;
RestTemplate 是 Spring 提供的用于訪問 Http 接口的客戶端框架,能夠大大提高客戶端的編寫效率,大部分基于 SSM 或 Spring Boot 的項目會使用 RestTemplate,翻了一下項目中 RestTemplate Config,只配置了超時時間:
public class RestTemplateConfig {public RestTemplate restTemplate(){SimpleClientHttpRequestFactory factory =new SimpleClientHttpRequestFactory();factory.setConnectTimeout(30000);factory.setReadTimeout(10000);RestTemplate restTemplate = new RestTemplate(factory);return restTemplate;}}
RestTemplate 默認(rèn)的?
ClientHttpRequestFactor?為
SimpleClientHttpRequestFactory,其中的 createRequest 方法,每次都會創(chuàng)建一個新的連接,建立連接本身就是一個費時費力的操作,如果頻繁地對一個接口發(fā)起調(diào)用,每次都創(chuàng)建連接會造成極大的資源浪費,而且若連接不能及時釋放,就會因為無法建立新的連接導(dǎo)致后面的請求阻塞。
public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException {HttpURLConnection connection = openConnection(uri.toURL(), this.proxy);prepareConnection(connection, httpMethod.name());if (this.bufferRequestBody) {return new SimpleBufferingClientHttpRequest(connection, this.outputStreaming);}else {return new SimpleStreamingClientHttpRequest(connection, this.chunkSize, this.outputStreaming);}}
看到這種情況處理起來也算簡單:弄個連接池;
創(chuàng)建 RestTemplate連接線程池,就需要使用其他的 HTTP 庫(默認(rèn)使用的是 JDK 自己的),比如 HttpComponents、Netty、OkHttp,在這里我選擇的是 HttpComponents 。
1. 引入依賴:
<dependency><groupId>org.apache.httpcomponentsgroupId><artifactId>httpcoreartifactId><version>version>dependency><dependency><groupId>org.apache.httpcomponentsgroupId><artifactId>httpclientartifactId><version>version>dependency>
2. 修改 RestTemplateConfig :
public?class?RestTemplateConfig?{public RestTemplate restTemplate() {return new RestTemplate(httpRequestFactory());}public ClientHttpRequestFactory httpRequestFactory() {return new HttpComponentsClientHttpRequestFactory(httpClient());}public HttpClient httpClient() {Registryregistry = RegistryBuilder. create() .register("http", PlainConnectionSocketFactory.getSocketFactory()).register("https", SSLConnectionSocketFactory.getSocketFactory()).build();PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(registry);//設(shè)置整個連接池最大連接數(shù)connectionManager.setMaxTotal(400);//路由是對maxTotal的細(xì)分connectionManager.setDefaultMaxPerRoute(100);RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(30000) //返回數(shù)據(jù)的超時時間????????????????.setConnectTimeout(10000)?//連接上服務(wù)器的超時時間.setConnectionRequestTimeout(1000) //從連接池中獲取連接的超時時間.build();return HttpClientBuilder.create().setDefaultRequestConfig(requestConfig).setConnectionManager(connectionManager).build();}}
修改完成之后再次壓測,單機(jī) TPS 穩(wěn)定在 1200,打完收工。
期待分享
如果您喜歡本文,請點個“在看”或分享到朋友圈,這將是對我最大的鼓勵。
