切換語言為:簡體
實際測試 SpringBoot 處理1 次 Http請求需要消耗多少伺服器記憶體?

實際測試 SpringBoot 處理1 次 Http請求需要消耗多少伺服器記憶體?

  • 爱糖宝
  • 2024-09-23
  • 2056
  • 0
  • 0

在實際工作中,經常會需要進行在全鏈路壓測,最佳化 GC引數,最佳化 JVM 記憶體分配。

當知道 1 次 RPC 請求和 Http 請求需要的堆記憶體大小後,你可以精確地計算:指定的併發量之下,系統需申請多少堆記憶體。同時結合 JVM 新生代堆大小,就能推算出 1 分鐘發生多少次 GC,這個 GC頻率是否過於頻繁?從而針對性的最佳化。

我們希望 1 次 Rpc、Http 請求申請堆記憶體足夠少,這樣可減少 GC 導致的系統停頓,提高系統性能,單機可以支撐更高的併發量。

1次 Http 請求,申請多少堆記憶體?

1 次 RPC 請求,申請多少堆記憶體?

如果不親自實驗,無法得出結論。

1. 實驗思路

關鍵動作

  1. 建立SpringBoot新應用(版本2.5.4)。

  2. 新增 Post 介面,供 JMeter 呼叫。

  3. JMeter(開源壓測工具)新建測試計劃。每個執行緒執行2000 次Http介面呼叫,共10 個執行緒,總呼叫 20000 次。

  4. SpringBoot 列印 GC 詳細日誌,記錄GC 前後,新生代申請了多少記憶體。

Jmeter 呼叫 20000 次 Http 介面以後,透過手動 GC 的方式觸發 GC,透過 GC 詳細日誌計算壓測期間新生代堆記憶體增長量。(物件基本分配在新生代)

2. SpringBoot 宣告 Http 介面

如下程式碼宣告了一個 Post介面 create; 建立了 Get 介面,用於觸發GC。

@Slf4j
@RestController
public class TestController {

   private AtomicLong count = new AtomicLong(0);

   @ResponseBody
   @RequestMapping(value = "create", method = RequestMethod.POST)
   public String create(@RequestBody Order order) {
      //log.warn("收到提單請求 cnt{}:{}", count.getAndIncrement(), order);
      return "ok";
   }

   @ResponseBody
   @RequestMapping(value = "gc", method = RequestMethod.GET)
   public String gc() {
      System.gc();
      return "ok";
   }
}


3. JMeter 新建測試計劃

3.1 新增執行緒組

新建執行緒組,選擇 10 個執行緒,每個執行緒迴圈 2000次。

實際測試 SpringBoot 處理1 次 Http請求需要消耗多少伺服器記憶體?

3.2 新建 Http 預設值

實際測試 SpringBoot 處理1 次 Http請求需要消耗多少伺服器記憶體?

3.3 新建請求頭

由於請求體是 JSON,所以新增請求頭 Content-Type

  實際測試 SpringBoot 處理1 次 Http請求需要消耗多少伺服器記憶體?

3.4 新建 Http 請求

請求中指定 Url 和 請求體 

實際測試 SpringBoot 處理1 次 Http請求需要消耗多少伺服器記憶體?

4. 實驗過程

4.1 啟動 SpringBoot應用

堆記憶體大小 4G,其中新生代記憶體 2G。SurivivorRadio=8,即每個 Surivivor 佔比新生代 1/10。

指定GC日誌位置:-Xloggc:/Users/testUser/log/gc.log

 java -server 
 -Xmx4g -Xms4g -XX:SurvivorRatio=8 -Xmn2g
 -XX:MetaspaceSize=512m -XX:MaxMetaspaceSize=1g -XX:MaxDirectMemorySize=1g 
 -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCCause -XX:+PrintGCDetails 
 -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+PrintTenuringDistribution 
 -XX:+UnlockDiagnosticVMOptions -XX:ParGCCardsPerStrideChunk=32768 
 -XX:+PrintCommandLineFlags -XX:+UseConcMarkSweepGC -XX:+UseParNewGC 
 -XX:ParallelCMSThreads=6 -XX:+CMSClassUnloadingEnabled 
 -XX:+UseCMSCompactAtFullCollection -XX:+CMSParallelInitialMarkEnabled 
 -XX:+CMSParallelRemarkEnabled -XX:+CMSScavengeBeforeRemark -XX:+PrintHeapAtGC 
 -XX:CMSFullGCsBeforeCompaction=1 -XX:CMSInitiatingOccupancyFraction=70 
 -XX:+UseCMSInitiatingOccupancyOnly -XX:+PrintReferenceGC  
 -XX:+ParallelRefProcEnabled -XX:ReservedCodeCacheSize=256M 
 -Xloggc:/Users/testUser/log/gc.log
 -jar target/activiti-0.0.1-SNAPSHOT.jar

4.2 多次手動 GC

由於JVM 啟動過程中,需要載入大量物件,所以我們在壓測之前先手動 GC,清理一下存量物件。

curl http://localhost:8080/gc

4.3 Jmeter 啟動壓測

執行 JMeter壓測計劃,每次執行會呼叫 20000 次,

  實際測試 SpringBoot 處理1 次 Http請求需要消耗多少伺服器記憶體?

4.4 GC 日誌解讀

GC 以後,新生代 Eden 區已使用記憶體為 0。 GC 前 Eden區大小就是 20000 次 Http 呼叫所申請的記憶體總和! 實際測試 SpringBoot 處理1 次 Http請求需要消耗多少伺服器記憶體?

5. 實驗結果

SpringBoot 在處理 Http 請求時,即使請求體相對較小,平均每次 Http 呼叫仍會申請約 34 K 的堆記憶體。這一點顯得尤為突出,因為請求體僅包含 50 個字元,遠遠未達到 1K 大小。然而,每次 Http 請求所消耗的記憶體卻依然高達 34K。這可能是由於在 SpringBoot 的內部處理流程中需要建立多個物件,這些物件的總記憶體佔用顯著高於請求體本身。

{"userId": 32898493, "productId":39043, "detail": ""}

在調整 Http 請求後,如將 detail 欄位設定為 1200 個字元時,每次 Http 呼叫平均佔用堆記憶體為 36K。兩次實驗結果間的差異為 2K,這與 1200 個字元佔用的記憶體大小基本持平(需考慮一定的誤差)。

這表明 SpringBoot 內部未進行多次請求體複製。

5.1 新增日誌列印

log.warn("收到提單請求 cnt{}:{}", count.getAndIncrement(), order);

在列印請求日誌後,單次 Http 請求的平均記憶體使用量達到了 56 KB,比之前增加了整整 20 KB。

然而,當我移除 detail 欄位後,單次請求的記憶體使用驟降至 35.7 KB。

這表明,當日志量較小時,列印日誌對記憶體佔用的影響較小。但隨著日誌大小的增加,記憶體佔用顯著上升,這可能觸發更頻繁地GC,最終導致系統性能明顯下降。

因此,建議各位嚴格控制單條日誌的大小,以最佳化記憶體使用和系統性能。

Case 記憶體消耗
小請求 34K
小請求 + 打日誌 35.7 K
大請求 36K
大請求 + 打日誌 56K

6. 真實的資料

根據以上實驗結果可得出結論:單次 HTTP 請求消耗約34KB記憶體,這並不意味著所有SpringBoot應用的記憶體消耗都是如此。由於實驗所用的程式碼相對簡單,因此34KB可能是記憶體消耗的最小值。

舉例來說,在我司的線上環境中,單次RPC請求的記憶體消耗在 0.5MB 到 1MB 之間,記憶體佔用量相對較大。

這是因為複雜的基礎架構、複雜的業務邏輯、複雜的流程、多次下游呼叫、多次SQL 呼叫、多次快取呼叫、日誌列印等等 均需要消耗大量的記憶體!

在此之前,我一直對新生代 Eden 區高達 5G 的情況下,仍每分鐘進行 1-2 次 young GC 感到困惑。

經過粗略計算後發現,如果每次請求消耗 0.5M 記憶體,當單臺伺服器每秒併發度達到 500 次時,每分鐘需要分配的記憶體高達 15G。因此,至少需要進行3次 young GC 才能滿足需求。

0則評論

您的電子郵件等資訊不會被公開,以下所有項目均必填

OK! You can skip this field.