1. 程式人生 > >效能監控: SPF4J介紹

效能監控: 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每分鐘下載量的線形圖。

 

 

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

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

 

 

現在我們就可以看到那些更低的值了。這些值在 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](search.maven.org/search?q=g:… 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](search.maven.org/search?q=g:… 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 框架的核心功能是記錄,聚合和儲存指標,以便在分析時不需要進行後置處理。它通過使用MeasurementRecorderMeasurementRecorderSource 類來實現。

這兩個類提供了兩種記錄度量的方法。關鍵的區別在於 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應用程式。然後,從選單中選擇 File> Open 。之後,讓我們使用瀏覽視窗找到 spf4j-performance-monitoring.tsdb2 檔案並開啟它。

我們現在可以看到一個新視窗,其中有一個包含我們的檔名和子專案的樹狀圖。讓我們點選子專案,然後點選它上面的 Plot按鈕。

這會生成一系列圖表。

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

 

 

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

 

 

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

 

 

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 – 允許該方法在沒有錯誤訊息的情況下執行的最長時間
  • recorderSourceMeasurementRecorderSource 的一個例項

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。

原文:www.baeldung.com/spf4j

作者:Mohan Sundararaju

譯者:KeepGoingPawn

 

&n