[ 用大白話講解複雜的技術 ]java
最近工做上有些調整,進入了另外一個項目組,本週在給一個接口作壓力測試的過程當中,發現 TPS 波動比較大:單臺服務器的 TPS 在 1000 到 200 之間波動,發現問題後咱們作了這幾件事兒逐一排問題。web
1. 排除其餘操做對壓測的影響數據庫
觀察數據的時間戳,發如今壓測過程當中有頻發的數據寫入,先暫停跑批操做,再次壓測波動依然存在;apache
2. 代碼檢查,下降數據庫查詢次數服務器
作代碼檢查,這個功能須要查詢六次數據庫、調用一次接口才能得到全部數據,通過優化後將查詢數據庫的次數下降爲三次,再次壓測後 TPS 有所提高,可是波動的狀況還存在;微信
3. 分階段壓測,縮小問題範圍app
由於該接口的邏輯是本地邏輯 + 遠程調用,因而先作了擋板單壓本地邏輯,再單獨壓遠程接口,兩次壓測不存在波動的問題,因此基本上能夠肯定是調用接口的過程當中存在一些問題;框架
4. 排除 GC 問題編輯器
調用接口的過程當中建立了太多的 Java 對象,可能會引起頻發的 GC 操做;可是經過對 JVM 的觀察排除了這個問題;ide
5. 初步確認問題
最後將問題的範圍縮小,懷疑是 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 默認的
ClientHttpRequestFactor 爲
SimpleClientHttpRequestFactory,其中的 createRequest 方法,每次都會建立一個新的鏈接,創建鏈接自己就是一個費時費力的操做,若是頻繁地對一個接口發起調用,每次都建立鏈接會形成極大的資源浪費,並且若鏈接不能及時釋放,就會由於沒法創建新的鏈接致使後面的請求阻塞。
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); }}
看到這種狀況處理起來也算簡單:弄個鏈接池;
建立 RestTemplate鏈接線程池,就須要使用其餘的 HTTP 庫(默認使用的是 JDK 本身的),好比 HttpComponents、Netty、OkHttp,在這裏我選擇的是 HttpComponents 。
1. 引入依賴:
<dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpcore</artifactId> <version></version></dependency>
<dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <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() { Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create() .register("http", PlainConnectionSocketFactory.getSocketFactory()) .register("https", SSLConnectionSocketFactory.getSocketFactory()) .build(); PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(registry); //設置整個鏈接池最大鏈接數 connectionManager.setMaxTotal(400); //路由是對maxTotal的細分 connectionManager.setDefaultMaxPerRoute(100); RequestConfig requestConfig = RequestConfig.custom() .setSocketTimeout(30000) //返回數據的超時時間 .setConnectTimeout(10000) //鏈接上服務器的超時時間 .setConnectionRequestTimeout(1000) //從鏈接池中獲取鏈接的超時時間 .build(); return HttpClientBuilder.create() .setDefaultRequestConfig(requestConfig) .setConnectionManager(connectionManager) .build(); }}
修改完成以後再次壓測,單機 TPS 穩定在 1200,打完收工。
期待分享
若是您喜歡本文,請點個「在看」或分享到朋友圈,這將是對我最大的鼓勵。
本文分享自微信公衆號 - 會點代碼的大叔(CodeDaShu)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。