記一次基於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
- 專案是一個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