1. 程式人生 > 程式設計 >記一次基於Docker的效能測試

記一次基於Docker的效能測試

一句話結論

對於跑在單核CPU上的運算類API,根據業務需求(最大響應時間)來除錯找到最大執行緒數,然後依據執行緒數除錯出heap大小(主要看年老代的回收次數)

若有理解不到位之處,請在評論區留言,跪謝!

背景

斷斷續續忙碌了幾個月,終於自己寫的開源專案算是有了雛形,打包成Docker image釋出到AWS EC2後,寫程式碼算是告一段落。隨之而來的問題就是“我的專案能夠支撐多少QPS” ,由於用了Docker,即變成了“我的專案基於Docker的配置能夠支撐多少QPS”,更進一步細化這個問題的話,有以下幾點:

  • 因為我用了IaaS來建立Linux伺服器(選用了Ubuntu)基本配置為 1G RAM 1CPU (2.5GHz)10G+ 硬碟空間(非SSD) 
  • 當然我並不希望一個Docker container就把上述資源全部佔用掉。另一個原因是目前IaaS所提供的記憶體最小單元是500M,算上系統其他程式的開銷,可供一個Docker image 的最大記憶體資源我定在了400M
說句題外話,我認為按照服務商提供的最小單元來劃分的好處在於:減少開銷。500M的費用只有1G的一半,而且將來專案動態伸縮的靈活度高,粒度更小。
  • 專案是一個OAuth2的Spring boot實現,本身對於IO的要求不高、都是短連結。因為要生成JWT令牌,主要壓力在CPU。用了內建的Tomcat,多執行緒在一個CPU上跑,請求數目一多,99%使用率簡直是家常便飯

第一步:確定效能測試的指標(benchmark)

俗話說的好,拋開業務需求來談IT就是耍流氓。

專案是開源專案,業務需求那就只好我自己定了,一般來說我們並不希望使用者登入過快(並且併發登入的情況雖然有但是確實比較少見),這次的api (oauth/token) 我定在了2秒的最大值,以此為基礎來找出效能瓶頸。

第二步:確定可調引數

那麼在不改動專案程式碼的前提下,可以調整哪些引數來提升效能的呢?

  • JVM 相關引數,例如 GC、Heap、Thread stack
  • Tomcat 相關引數,例如 max-threads、max-connections、accept-count

對於計算為主的專案,主要關注點還是在max-threads,設定合理的話可以減少CPU上下文切換帶來的效能開銷,以及合適的Heap大小來避免頻繁觸發GC所造成效能抖動。

第三步:開測 (多圖預警)

雖然標題是基於Docker的效能測試,但是我還是想對比一下用與不用Docker上的效能差異,所以測試會分為兩個部分,以下為jre的基本資訊

ubuntu@ip-xxx-xx-xx-xxx:~$ java -version
openjdk version "11.0.4" 2019-07-16
OpenJDK Runtime Environment (build 11.0.4+11-post-Ubuntu-1ubuntu218.04.3)
OpenJDK 64-Bit Server VM (build 11.0.4+11-post-Ubuntu-1ubuntu218.04.3,mixed mode,sharing)複製程式碼

ubuntu jre 資訊

/ # java -version
openjdk version "11" 2018-09-25
OpenJDK Runtime Environment 18.9 (build 11+28)
OpenJDK 64-Bit Server VM 18.9 (build 11+28,mixed mode)複製程式碼

docker jre 資訊

3.1 無Docker下的效能測試

3.1.1 Heap 固定,不同執行緒數關係圖




50m Heap大小在100個執行緒的情況下OOM,所以這裡為0

3.1.2 執行緒數固定,不同Heap大小下關係圖




3.2 基於Docker的效能測試

直接上對比圖

上圖為不限制Docker 容器大小的結果

上圖限制了Docker 容器大小為450M

3.3 垃圾回收次數(1000QPS)



上圖100 threads 50m Heap 的時候程式直接崩潰了所以為0

第四步:分析

4.1 Docker vs 非 docker

很明顯,Docker會帶來一定的效能開銷,並且隨著執行緒數的增加與QPS的增加,這種開銷會更加明顯。但是並不是說Docker不好,畢竟效能開銷只要多開一個節點就搞定,和Docker帶來的便利性相比,幾乎可以無視。

4.2 執行緒數 與 JVM heap

這裡的討論僅限於單核CPU負載較高的運算類API,Serial GC

  • 雖然執行緒數越多吞吐量越高,但是響應時間會更快的增長
  • Heap過小會導致頻繁的垃圾回收(年輕代影響較小,年老代最為突出)甚至會OOM導致程式崩潰
  • Heap過小時,執行緒數越大,年老代的回收次數顯著增多,年輕代反而會降低(年老代回收為主力)
  • Heap過大並不會帶來效能提升,但是年輕代回收次數會顯著減少,而年老代幾乎不受影響

4.3 一句話結論

對於跑在單核CPU上的運算類API, 根據業務需求(最大響應時間)來除錯找到最大執行緒數,然後依據執行緒數除錯出heap大小(主要看年老代的回收次數)

4.4 回到我的開源專案

很簡單,10 threads 100m heap