一次性能優化:吞吐量從1提高到2500

性能優化,簡而言之,就是在不影響系統運行正確性的前提下,使之運行地更快,完成特定功能所需的時間更短。壓測也是檢驗一個架構設計是否合理的一個重要方法。

項目介紹

這個項目是一個線下支付的交易系統,使用線下設備發起支付。項目使用SpringMVC+Dubbo的微服務開發模式,其中SpringMvc做爲Web端部署在tomcat上對外提供rest服務,其餘模塊使用dubbo開發,SpringMVC調用dubbo服務完成業務功能。
圖片描述mysql

優化目的

這一次的性能優化,目的是在不調整業務邏輯的狀況下經過壓力測試的方式測試下單時接口的性能能達到多少,若是有性能問題,就要在不修改業務邏輯的狀況下經過優化各項參數的方式(包括JVM優化、數據庫鏈接數和Dubbo鏈接數)來提高總體性能。git

壓測前的準備

壓力測試策略sql

採用按部就班的方式,逐漸增長併發量的方式,先以1個併發開始,慢慢增長,當達到瓶頸的時候就進行參數調整和優化。數據庫

創建壓測分支tomcat

壓測優化過程當中,可能隨時須要調整配置參數,可能會對一些配置參數進行修改或者對公共代碼進行優化。爲了避免讓其餘研發人員的開發受到干擾,在git上基於release分支創建了feature_stress分支,專門用來壓測過程當中,參數配置調優使用,不影響其餘研發人員的開發。性能優化

搭建壓測環境服務器

爲了避免不影響研發和測試人員的工做,單獨申請了三臺4C32G的虛擬機進行壓力測試,一臺部署tomcat,一臺部署dubbo服務,一臺部署mysql。爲何只申請單機部署的方式壓測呢?由於本次測試的目的是看單臺服務器的性能狀況,優化的目的是將單臺服務器的性能最大化。多線程

準備測試腳本架構

本次測試選用jmeter做爲測試工具進行壓力測試,由測試人員模擬線下支付的流程寫好測試腳本。併發

初步壓測

很保守的,從1個併發開始壓測,測試結果讓人很是的驚喜,壓測開始幾分鐘後,就出現大量的鏈接失敗,沒法繼續測試。出現大量的內存溢出的異常。沒出現異常時的吞吐量達到了1req/s。同時,經過jconsole控制檯監控tomcat,發現其線程數最高到了2萬多個。
圖片描述

問題分析

這個異常是建立本地線程失敗拋出的異常,爲何有這麼大量的線程呢?不合常理呀!因而查看對應的代碼,原來這是一個自定義的一我的日誌Appender,在這個Appender裏使用了線程池,這個Appender原本的目的是使用多線程提高日誌性能,而且將全部的日誌都收集到一個文件中。代碼相似以下:
圖片描述
log4j.properties中直接將新的Appender添加到了rootLogger下:
圖片描述

初步優化

經過以上代碼和日誌配置文件就能看出來,每第二天志輸出都會建立線程,這就是線程爲何愈來愈多,最後致使沒法建立新的本地線程的緣由。

爲了避免影響現有的功能,將LogAppender類append方法中的線程池建立的部分變成了類的屬性,並固定只用8個線程:

圖片描述

提交代碼從新部署壓測環境,再進行壓測,吞吐量立刻就上來了,達到了1700req/s,

![圖片描述][7]

深刻調優

通過上面的優化以後,總體的性能還不是很高,通過jconsole監控發現,線程數仍是很高,雖然沒有2萬那麼多了,但仍是有3000多的線程,這麼多的線程根本沒法發揮機器的性能,時間都浪費在線程切換上了,經過查看線程棧發現大量的線程都是dubbo鏈接的線程,爲何如此之多呢?查看dubbo的配置文件,consumer的連接數都設置成了1000,因而將全部dubbo的consumer鏈接數均改成了64。同時將log4j.properties中配置了控制檯輸出的都關閉了。

最終的優化後的測試結果,最高達到了2600req/s。
圖片描述

總結

性能優化須要從幾個方面考慮:

  1. CPU是否有瓶頸,本項目沒有大量的計算,因此CPU沒有瓶頸。
  2. IO是不是瓶頸,線程太多或者鏈接太多都是瓶頸,本項目中併發時建立了大量的線程,達到了服務器的最高可用的鏈接數,致使內存溢出了,建立太多的線程也會讓CPU把時間都浪費在線程切換上了。
  3. 生產環境不要使用控制檯輸出,由於控制檯輸出是同步的,輸出太多會對性能有很大的影響。
  4. 若是出現吞吐量小的狀況能夠輸出線程棧,看看究竟是block在哪裏了,是調用服務時間長仍是讀寫數據庫時間長。

———— / END / ————

相關文章
相關標籤/搜索