性能監控: SPF4J介紹


1. 總體介紹

性能測試是一項在軟件生命開發周期中總是被置於最后一環的活動。我們經常依靠

Java profilers 去幫助發現性能問題。

在這篇文章中,我們將會學習關於 Java 的簡單性能測試框架 - SPF4J。它提供了可以加在我們代碼中的 API。因此,我們可以將 性能監視變為我們組件的一部分

2. 度量捕獲和可視化的基本概念

在我們開始之前,讓我們用一個簡單的例子來理解度量捕獲和可視化的基本概念。

讓我們想象一下:我們正關注於一款新發布的 App 在應用商店的下載量,出於學習的目的,讓我們手工的做這件事情。

2.1. 捕捉度量

首先我們需要決定要測量什么,我們感興趣的是每分鍾下載量。 因此*,* 我們將會測量下載量的數量。

第二,我們多久需要執行一次測量?讓我們 “每分鍾測量一次”吧。

最后,我們應該監控多長時間?讓我們 “監控一小時吧”。

有了以上的這些規則,我們就可以實施這個實驗了。當實驗完成的時候,我們可以看到以下的結果:

時間    累積下載量                   每分鍾下載數
----------------------------------------------
T       497                     0  
T+1     624                     127
T+2     676                     52
...     
T+14    19347                   17390
T+15    19427                   80
...  
T+22    27195                   7350
...  
T+41    41321                   11885
...   
T+60    43395                   40

前兩列-時間 和累積下載數– 我們可以很直觀的看到這些值。第三列,每分鍾下載量,是一個由當前和之前累積下載量的差額計算出來的間接值。我們看到了那個時間段的實際下載數。

2.2. 可視化度量

讓我們繪制一個關於 時間 vs每分鍾下載量的線形圖。

file

我們可以看到,有一些峰值表明大量的下載發生在一些場合。因為使用線性比例作為下載量軸,所以較低的值以直線出現。

讓我們用以 10 為底的對數作為下載量軸的標度,並繪制一個對數/線性圖。

file

現在我們就可以看到那些更低的值了。這些值在 100 上下浮動。注意,線性圖中的平均值為 703 ,因為它也包含峰值。

如果我們將峰值從圖像中移除,我們可以使用對數/線形圖從我們的實驗中得到結論:

  • 每分鍾下載量的平均值在 100 左右

3. 函數調用的性能監視

在理解了如何捕獲一個簡單的度量並從前面的例子中分析之后,讓我們現在將它應用於一個簡單的 Java 方法 - 是否是素數

private static boolean isPrimeNumber(long number) {
    for (long i = 2; i <= number / 2; i++) {
        if (number % i == 0)
            return false;
    }
    return true;
}

使用 SPF4J,有兩種方法可以捕獲指標。讓我們在下一節中探討它們。

4. 設置和配置

4.1. Maven 設置

SPF4J 為不同的性能測試提供了許多不同的庫,但我們只需要一些簡單的例子。

核心庫是 [spf4j-core](https://search.maven.org/search?q=g:org.spf4j AND a:spf4j-core),它為我們提供了大部分必要的功能。

讓我們將其添加到 Maven 依賴:

<dependency>
    <groupId>org.spf4j</groupId>
    <artifactId>spf4j-core</artifactId>
    <version>8.6.10</version>
</dependency>

有一個更適合性能監控的庫 - *spf4j-aspects,*它使用的是 AspectJ

我們將在我們的示例中探討這一點,所以我們也添加它:

<dependency>
    <groupId>org.spf4j</groupId>
    <artifactId>spf4j-aspects</artifactId>
    <version>8.6.10</version>
</dependency>

最后,SPF4J 還帶有一個對數據可視化非常有用的簡單 UI,所以讓我們添加 [spf4j-ui](https://search.maven.org/search?q=g:org.spf4j AND a:spf4j-ui) 如下:

<dependency>
    <groupId>org.spf4j</groupId>
    <artifactId>spf4j-ui</artifactId>
    <version>8.6.10</version>
</dependency>

4.2. 輸出文件的配置

SPF4J 框架將數據寫入時間序列數據庫(TSDB),也可以選擇寫入文本文件。

讓我們配置它們並設置系統屬性 spf4j.perf.ms.config

public static void initialize() {
  String tsDbFile = System.getProperty("user.dir") + File.separator + "spf4j-performance-monitoring.tsdb2";
  String tsTextFile = System.getProperty("user.dir") + File.separator + "spf4j-performance-monitoring.txt";
  LOGGER.info("\nTime Series DB (TSDB) : {}\nTime Series text file : {}",tsDbFile,tsTextFile);
  System.setProperty("spf4j.perf.ms.config","TSDB@" + tsDbFile + "," + "TSDB_TXT@" + tsTextFile);
}

4.3. 記錄器和來源

SPF4J 框架的核心功能是記錄,聚合和保存指標,以便在分析時不需要進行后置處理。它通過使用MeasurementRecorder 和 MeasurementRecorderSource 類來實現。

這兩個類提供了兩種記錄度量的方法。關鍵的區別在於 MeasurementRecorder 可以從任何地方調用,而MeasurementRecorderSource 僅用於注解。

該框架為我們提供了一個 RecorderFactory 工廠類,用於為不同類型的聚合創建記錄器和記錄器源類的實例:

  • createScalableQuantizedRecorder() 和 createScalableQuantizedRecorderSource()
  • createScalableCountingRecorder() 和 createScalableCountingRecorderSource()
  • createScalableMinMaxAvgRecorder() 和 createScalableMinMaxAvgRecorderSource()
  • createDirectRecorder() 和 createDirectRecorderSource()

對於我們的示例,讓我們選擇可擴展的量化聚合。

4.4. 創建記錄器

首先,讓我們創建一個輔助方法來創建 MeasurementRecorder 的實例:

public static MeasurementRecorder getMeasurementRecorder(Object forWhat) {
    String unitOfMeasurement = "ms";
    int sampleTimeMillis = 1_000;
    int factor = 10;
    int lowerMagnitude = 0;
    int higherMagnitude = 4;
    int quantasPerMagnitude = 10;

    return RecorderFactory.createScalableQuantizedRecorder(
      forWhat,unitOfMeasurement,sampleTimeMillis,factor,lowerMagnitude,
      higherMagnitude,quantasPerMagnitude);
}

我們來看看不同的參數意思:

  • unitOfMeasurement – 被測量的單位價值 - 對於性能檢測方案,它通常是一個時間單位
  • sampleTimeMillis – 進行測量的時間段 - 換句話說,進行測量的頻率
  • factor – 用於繪制測量值的對數標度的底數
  • lowerMagnitude – 對數刻度的最小值 - 對於底數 10,lowerMagnitude = 0 表示 10 的 0 次冪 = 1
  • higherMagnitude – 對數刻度上的最大值 - 對於底數 10,higherMagnitude = 4 表示 10 的 4 次冪 = 10,000
  • quantasPerMagnitude – 幅度范圍內的部分數量 - 如果幅度范圍從 1,000 到 10,000,則 quantasPerMagnitude = 10 表示范圍將被划分為 10 個子范圍

我們可以看到可以根據需要更改值。因此,為不同的測量創建單獨的 MeasurementRecorder 實例可能是個好主意。

4.5. 創建資源

接下來,讓我們使用另一個輔助方法創建 MeasurementRecorderSource 的實例:

public static final class RecorderSourceForIsPrimeNumber extends RecorderSourceInstance {
    public static final MeasurementRecorderSource INSTANCE;
    static {
        Object forWhat = App.class + " isPrimeNumber";
        String unitOfMeasurement = "ms";
        int sampleTimeMillis = 1_000;
        int factor = 10;
        int lowerMagnitude = 0;
        int higherMagnitude = 4;
        int quantasPerMagnitude = 10;
        INSTANCE = RecorderFactory.createScalableQuantizedRecorderSource(
          forWhat,unitOfMeasurement,sampleTimeMillis,factor,
          lowerMagnitude,higherMagnitude,quantasPerMagnitude);
    }
}

請注意,我們使用了與之前相同的設置值。

4.6. 創建配置類

現在讓我們創建一個 Spf4jConfig 類並將所有上述方法放入其中:

public class Spf4jConfig {
    public static void initialize() {
        //...
    }

    public static MeasurementRecorder getMeasurementRecorder(Object forWhat) {
        //...
    }

    public static final class RecorderSourceForIsPrimeNumber extends RecorderSourceInstance {
        //...
    }
}

4.7. 配置 aop.xml

SPF4J 為我們提供了基於注解的性能測量和監控方法的選項。它使用 AspectJ 庫,它允許在不修改代碼本身的情況下向現有代碼添加性能監視所需的其他行為。

讓我們使用 load-time weaver 編織我們的類和切面,並將 aop.xml 放在 META-INF 文件夾下:

<aspectj>
    <aspects>
        <aspect name="org.spf4j.perf.aspects.PerformanceMonitorAspect" />
    </aspects>
    <weaver options="-verbose">
        <include within="com..*" />
        <include within="org.spf4j.perf.aspects.PerformanceMonitorAspect" />
    </weaver>
</aspectj>

5. 使用 MeasurementRecorder

現在讓我們看看如何使用 MeasurementRecorder 來記錄測試功能的性能指標。

5.1. 記錄指標

讓我們生成 100 個隨機數並在循環中調用是否為素數方法。在此之前,讓我們調用我們的 Spf4jConfig 類來進行初始化並創建 MeasureRecorder 類的實例。使用此實例,讓我們調用 record() 方法來保存調用 100 次 isPrimeNumber() 所需的時間:

Spf4jConfig.initialize();
MeasurementRecorder measurementRecorder = Spf4jConfig
  .getMeasurementRecorder(App.class + " isPrimeNumber");
Random random = new Random();
for (int i = 0; i < 100; i++) {
    long numberToCheck = random.nextInt(999_999_999 - 100_000_000 + 1) + 100_000_000;
    long startTime = System.currentTimeMillis();
    boolean isPrime = isPrimeNumber(numberToCheck);
    measurementRecorder.record(System.currentTimeMillis() - startTime);
    LOGGER.info("{}. {} is prime? {}",i + 1,numberToCheck,isPrime);
}

5.2. 運行代碼

我們已經准備好測試我們的簡單函數 isPrimeNumber() 的性能。

讓我們運行代碼並查看結果:

Time Series DB (TSDB) : E:\Projects\spf4j-core-app\spf4j-performance-monitoring.tsdb2
Time Series text file : E:\Projects\spf4j-core-app\spf4j-performance-monitoring.txt
1. 406704834 is prime? false
...
9. 507639059 is prime? true
...
20. 557385397 is prime? true
...
26. 152042771 is prime? true
...
100. 841159884 is prime? false

5.3. 查看結果

讓我們在項目文件夾下通過運行命令來啟動 SPF4J UI:

java -jar target/dependency-jars/spf4j-ui-8.6.9.jar

這將打開桌面UI應用程序。然后,從菜單中選擇 FileOpen 。之后,讓我們使用瀏覽窗口找到 spf4j-performance-monitoring.tsdb2 文件並打開它。

我們現在可以看到一個新窗口,其中有一個包含我們的文件名和子項目的樹狀圖。讓我們點擊子項目,然后點擊它上面的 Plot按鈕。

這會生成一系列圖表。

第一個圖表測量分布是我們之前看到的對數線性圖的變體。該圖還顯示了基於計數的熱圖。

file

第二個圖表顯示聚合數據,如最小值,最大值和平均值:

file

最后一張圖顯示了測量次數與時間的關系:

file

6. 使用 MeasurementRecorderSource

在上一節中,我們必須圍繞我們的功能編寫額外的代碼來記錄測量值。在本節中,讓我們使用另一種方法來避免這種情況。

6.1. 記錄指標

首先,我們將刪除為捕獲和記錄指標而添加的額外代碼:

Spf4jConfig.initialize();
Random random = new Random();
for (int i = 0; i < 50; i++) {
    long numberToCheck = random.nextInt(999_999_999 - 100_000_000 + 1) + 100_000_000;
    isPrimeNumber(numberToCheck);
}

接下來,讓我們用 @PerformanceMonitor 來注解 isprimenumber() 方法,而不是所有的樣板文件:

@PerformanceMonitor(
  warnThresholdMillis = 1,
  errorThresholdMillis = 100,
  recorderSource = Spf4jConfig.RecorderSourceForIsPrimeNumber.class)
private static boolean isPrimeNumber(long number) {
    //...
}

讓我們看看這些參數:

  • warnThresholdMillis – 允許該方法在沒有警告消息的情況下運行的最長時間
  • errorThresholdMillis – 允許該方法在沒有錯誤消息的情況下運行的最長時間
  • recorderSource – MeasurementRecorderSource 的一個實例

6.2. 運行代碼

讓我們先做一個 Maven 構建,然后通過傳遞 Java 代理來執行代碼:

java -javaagent:target/dependency-jars/aspectjweaver-1.8.13.jar -jar target/spf4j-aspects-app.jar

看下結果:

Time Series DB (TSDB) : E:\Projects\spf4j-aspects-app\spf4j-performance-monitoring.tsdb2
Time Series text file : E:\Projects\spf4j-aspects-app\spf4j-performance-monitoring.txt

[DEBUG] Execution time 0 ms for execution(App.isPrimeNumber(..)),arguments [555031768]
...
[ERROR] Execution time  2826 ms for execution(App.isPrimeNumber(..)) exceeds error threshold of 100 ms,arguments [464032213]
...

我們可以看到 SPF4J 框架記錄了每次方法調用所花費的時間。只要它超過 errorThresholdMillis 值100毫秒,它就會將其記錄為錯誤。傳遞給該方法的參數也會被記錄。

6.3. 查看結果

我們可以使用與之前使用 SPF4J UI 相同的方式查看結果,因此我們可以參考上一節。

7. 結論

在本文中,我們討論了捕獲和可視化指標的基本概念。然后,我們借助一個簡單的例子了解了 SPF4J 框架的性能監控功能。我們還使用內置的 UI 工具來可視化數據。與往常一樣,本文中的示例均可用 over on GitHub

原文:https://www.baeldung.com/spf4j

作者:Mohan Sundararaju

譯者:KeepGoingPawn


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM