[17.0.0.3 以及更新版本]

MicroProfile 度量檢測 API

您可以使用 MicroProfile 度量 API,在應用程式中新增度量。MicroProfile 度量 API 類似於 Dropwizard 度量 API。

MicroProfile 度量的架構

mpMetrics-1.0mpMetrics-1.1 特性提供三項用來儲存度量的度量登錄。
基本
基本登錄用於來自伺服器(必要的話,還有來自 MicroProfile 度量規格)的度量。
供應商
供應商登錄用於產品供應商(以本例來說是 Liberty)所新增的度量。
應用程式
應用程式登錄用於您應用程式所新增的度量。應用程式登錄是預設 MetricRegistry。

mpMetrics-1.0mpMetrics-1.1 特性還包含 /metrics 端點,它提供 HTTP 介面,以便與瀏覽器和監視工具互動。

檢查度量的值

MicroProfile 度量提供 /metrics 端點,您可以透過 HTTP 來存取。在使用適當的配置啟動 Liberty 伺服器之後,您可以從任何瀏覽器來檢視度量。

下列 URL 格式範例說明一個位址,您可以在其中檢視您的度量:
https://localhost:9443/metrics

一般而言,作業團隊會配置一些工具,以便監視及記錄某段時間的度量值。

新增度量至應用程式

如果要新增度量至應用程式,您必須使用應用程式登錄,來建立及登錄度量,以便讓系統能識得這些度量,並可從 /metrics 端點來報告它們。您可以採用下列方式來建立及登錄度量:
  • 直接使用 MetricRegistry。當使用此方法時,您的應用程式碼會明確建立度量,並登錄它們。
  • 使用 CDI 來注入度量。當使用此方法時,CDI 會隱含建立度量,並利用應用程式 MetricRegistry 來登錄。
  • 使用度量註釋。當使用此方法時,CDI 會隱含建立度量,並利用應用程式 MetricRegistry 來登錄。

直接管理 MetricRegistry

MetricRegistry 儲存了所有度量和其個別的 meta 資料。可以利用所提供的方法,來登錄及擷取度量和 meta 資料。依預設,與應用程式有關的所有度量會新增至以單一應用程式為範圍的 MetricRegistry 中。

應用程式度量登錄可透過 CDI 注入取得。
@Inject
MetricRegistry registry;

登錄度量

MetricRegistry 類別具有一些方法,可用來擷取或建立計數器、計量、直方圖和計時器。

登錄會檢查是否存在同名的度量。如果度量已存在,就會傳回它。
Metadata counterMetadata = new Metadata(…);
…
 
// Create or retrieve the metric by Metadata.getName()
Counter counterMetric = registry.counter(counterMetadata);
Timer timerMetric =  registry.timer(timerMetadata);
Histogram histogramMetric =  registry.histogram(histogramMetadata);
Meter meterMetric =  registry.meter(meterMetadata);
 
// Register a gauge metric by Metadata.getName()
registry.register("gaugeMetric", gaugeMetric, gaugeMetadata);

以 meta 資料來說明度量

meta 資料是一組資料,用來說明與及彙總度量的相關資訊,並且可讓您更容易找到及使用不同類型的度量。meta 資料可以精細到足以在屬性層次定義資訊。

度量的 meta 資料由下列屬性組成:

meta 資料能適度說明每一個度量的用途。當您透過 REST API 存取度量時,此資訊就很有用。
單位
一組固定的字串單位。
類型
量規、計數器、計量、直方圖或計時器。
說明
人類可讀的度量說明。
DisplayName
人類可讀的顯示用度量名稱(如果該度量名稱非人所能判讀,例如,當度量名稱是 UUID 時)。
標籤
鍵值(鍵=值)配對清單,以逗點區隔。
[18.0.0.1 以及更新版本]可重複使用
[18.0.0.1 以及更新版本]在 MicroProfile 度量 1.1 版和更新版本中,可重複使用是一個布林值,指出該度量名稱可重複使用。例如,您想重複使用單一度量,而同時您又標註了多個方法。
若為廣域或應用程式層面的標籤,其設定方式是將鍵=值配對清單(以逗點區隔),放在 MP_METRICS_TAGS 環境變數中傳遞。
export MP_METRICS_TAGS=app=shop,node=2932

[18.0.0.1 以及更新版本]您也可以使用 MicroProfile 配置,來配置 MP_METRICS_TAGS。如需如何配置 MP_METRICS_TAGS 的相關資訊,請參閱Liberty 執行時期中有效的配置值位置

在處理程序的整個生命期限中,meta 資料都無法變更。例如,它無法在某次擷取中傳回秒單位,而在另一次擷取中傳回小時單位。
註: 如果沒有提供 DisplayName,則度量的預設名稱會是物件的名稱。

計數器

計數器是一種度量,用來保留增量或減量的計數。計數器的起始值會設為 0,可藉由 inc()inc(long n) 來增量,以及藉由 dec()dec(long n) 來減量。

您可以使用計數器,來計數所收到的要求總數,或計數同時作用中的 HTTP 階段作業總數。

下列範例使用計數器,來測量 /donations REST 端點的命中率。
Metadata statsHitsCounterMetadata = new Metadata(
    "statsHits",                                // name
    "Stats Hits",                               // display name
    "Number of hits on the /stats endpoint",    // description
    MetricType.COUNTER,                         // type
    MetricUnits.NONE);                          // units
 
Counter statsHitsCounter = registry.counter(statsHitsCounterMetadata);
@GET
@Path("/donations")
public String getTotalDonations() {
    statsHitsCounter.inc();
    return "$" + donationManager.getTotalDonations();
}
以下是 REST 端點產生的結果。
curl -k -u user:password https://localhost:9443/metrics/application/statsHits
# TYPE application:stats_hits counter
# HELP application:stats_hits Number of hits on the /stats endpoint
application:stats_hits 213

curl -k -u user:password -H “Accept: application/json” https://localhost:9443/metrics/application/statsHits
{"statsHits":213}

量規

量規代表取樣的度量,以取得其值。

量規是一個介面,需由開發人員實作。由於未定義量規的實作,必須使用 MetricRegistry .register() 方法,手動向 MetricRegistry 登錄它們。請確定對於每一個度量,只使用一次 register()。不能登錄兩個同名的度量。您將使用量規,來測量 CPU 溫度,或測量磁碟用量。

下列範例說明量規,它以距離抵達目標的百分比形式,來表示目前的進度。
Gauge<Double> progressGauge = new Gauge<Double>() {
    public Double getValue() {
        return (double) getTotalDonations()
             / (double) getGoal() * 100.0;
    }
};
 
// Gauge
Metadata progressMetadata = new Metadata(
    "progress",                                     // name
    "Donation Progress",                            // display name
    "The percentage of the goal achieved so far",   // description
    MetricType.GAUGE,                               // type
    MetricUnits.PERCENT);                           // units
 
registry.register(progressMetadata.getName(), progressGauge, progressMetadata);
以下是 REST 端點產生的結果。
curl -k -u user:password https://localhost:9443/metrics/application/progress
# TYPE application:progress_percent gauge
# HELP application:progress_percent The percentage of the goal achieved so far
application:progress_percent 4.472

curl -k -u user:password -H "Accept: application/json" https://localhost:9443/metrics/application/progress
{"progress":4.472}

計量

計量用來追蹤傳輸量。

如果要使用計量,必須呼叫 meter.mark() 方法,以標示事件。若有多個事件,您也可以使用 mark(long n),來同時標示這些事件的多個出現處。計量會提供下列資訊:
  • 平均傳輸量。
  • 一/五/十五分鐘的指數加權移動平均傳輸量。
  • 測量數的計數。

您可以使用計量,來計算應用程式正在處理的交易速率。

下列範例使用計量,來判定 getProcess() 的呼叫速率。速率越高可能表示需要快取結果,或是需要制定速率限制。
Metadata getProgressMeterMetadata = new Metadata(
    "getProgressMeter",                             // name
    "getProgress Call Rate",                        // display name
    "The rate of getProgress calls",                // description
    MetricType.METERED,                             // type
    MetricUnits.SECONDS);                           // units
Meter getProgressMeter = registry.meter(getProgressMeterMetadata);
public Double getProgress() {
    getProgressMeter.mark();
    return (double) getTotalDonations()/(double) getGoal() * 100.0;
}
以下是 REST 端點產生的結果。
curl -k -u user:password https://localhost:9443/metrics/application/getProgressMeter1
# TYPE application:get_progress_meter_total counter
# HELP application:get_progress_meter_total The rate of getProgress calls
application:get_progress_meter_total 78
# TYPE application:get_progress_meter_rate_per_second gauge
application:get_progress_meter_rate_per_second 0.6584919803150174
# TYPE application:get_progress_meter_one_min_rate_per_second gauge
application:get_progress_meter_one_min_rate_per_second 0.5851884005757912
# TYPE application:get_progress_meter_five_min_rate_per_second gauge
application:get_progress_meter_five_min_rate_per_second 1.4218610926179416
# TYPE application:get_progress_meter_fifteen_min_rate_per_second gauge
application:get_progress_meter_fifteen_min_rate_per_second 1.6627979138032118

curl -k -u user:password -H "Accept: application/json" https://localhost:9443/metrics/application/getProgressMeter
	{
   "getProgressMeter": {
       "count": 78,
       "fifteenMinRate": 1.6627979138032118,
       "fiveMinRate": 1.4218610926179416,
       "meanRate": 0.6584919803150174,
       "oneMinRate": 0.5851884005757912
   }
}

直方圖

直方圖用來儲存值的分佈。

如果要將值記錄在直方圖中,您必須呼叫具有您想記錄之值的 histogram.update(long value)。直方圖的現行狀態(或 Snapshot)可以利用 getSnapshot() 來擷取。「MicroProfile 度量」中的直方圖只支援整數或長整數值。

直方圖會提供下列資訊:
  • 最大值/最小值/平均值
  • 位於第 50、75、95、98、99 和 99.9 個百分位的值
  • 值數目的計數

直方圖範例包含所擷取之有效負載大小的分佈,或是員工到任意見調查(收集了家庭收入的分佈)之有效負載大小的分佈。

下列範例說明用來儲存捐獻值的直方圖。此範例為管理者提供了一個捐獻金額的分佈構想。
Metadata donationDistributionMetadata = new Metadata(
    "donationDistribution",                      // name
    "Donation Distribution",                     // display name
    "The distribution of the donation amounts ", // description
    MetricType.HISTOGRAM,                        // type
    “dollars”);                                  // units
Histogram donationDistribution = registry.histogram(donationDistributionMetadata);
public void addDonation(Long amount) {
    totalDonations += amount;
    donations.add(amount);
    donationDistribution.update(amount);
}
以下是 REST 端點產生的結果:
curl -k -u user:password https://localhost:9443/metrics/application/com.example.samples.donationapp.DonationManager.donationDistribution

# TYPE application:com_example_samples_donationapp_donation_manager_donation_distribution_mean_dollars gauge
application:com_example_samples_donationapp_donation_manager_donation_distribution_mean_dollars 19.300015535407777
# TYPE application:com_example_samples_donationapp_donation_manager_donation_distribution_max_dollars gauge
application:com_example_samples_donationapp_donation_manager_donation_distribution_max_dollars 102.0
# TYPE application:com_example_samples_donationapp_donation_manager_donation_distribution_min_dollars gauge
application:com_example_samples_donationapp_donation_manager_donation_distribution_min_dollars 3.0
# TYPE application:com_example_samples_donationapp_donation_manager_donation_distribution_stddev_dollars gauge
application:com_example_samples_donationapp_donation_manager_donation_distribution_stddev_dollars 26.35464238355834
# TYPE application:com_example_samples_donationapp_donation_manager_donation_distribution_dollars summary
# HELP application:com_example_samples_donationapp_donation_manager_donation_distribution_dollars The distribution of the donation amounts
application:com_example_samples_donationapp_donation_manager_donation_distribution_dollars_count 203
application:com_example_samples_donationapp_donation_manager_donation_distribution_dollars{quantile="0.5"} 5.0
application:com_example_samples_donationapp_donation_manager_donation_distribution_dollars{quantile="0.75"} 24.0
application:com_example_samples_donationapp_donation_manager_donation_distribution_dollars{quantile="0.95"} 83.0
application:com_example_samples_donationapp_donation_manager_donation_distribution_dollars{quantile="0.98"} 93.0
application:com_example_samples_donationapp_donation_manager_donation_distribution_dollars{quantile="0.99"} 101.0
application:com_example_samples_donationapp_donation_manager_donation_distribution_dollars{quantile="0.999"} 102.0

curl -k -u user:password -H "Accept: application/json" https://localhost:9443/metrics/application/com.example.samples.donationapp.DonationManager.donationDistribution
	{
    "com.example.samples.donationapp.DonationManager.donationDistribution": {
        "count": 203,
        "max": 102,
        "mean": 19.300015535407777,
        "min": 3,
        "p50": 5.0,
        "p75": 24.0,
        "p95": 83.0,
        "p98": 93.0,
        "p99": 101.0,
        "p999": 102.0,
        "stddev": 26.35464238355834
    }
}

計時器

計時器用來聚集計時持續時間(以奈秒為單位),並提供持續時間和傳輸量的統計資料。

如果要針對部分程式碼來計時,您可以呼叫 timer.time(),這會傳回 timer.context 物件。會藉由呼叫 context.close(),使用這個環境定義來停止計時器。擷取自計時器的資訊是由計時持續時間的計量與直方圖組合而成。

計時器會提供下列資訊:
  • 最長/最短/平均時間。
  • 位於第 50、75、95、98、99 和 99.9 個百分位的時間值。
  • 平均傳輸量。
  • 一/五/十五分鐘的指數加權移動平均傳輸量。
  • 計時事件數目的計數。

計時器用途的相關範例包括:測量回應時間,以及測量複雜邏輯的處理時間。

下列範例說明計時器,它用來測量在捐獻中進行線性搜尋的執行時間:
Metadata topDonationCalcTimerMetadata = new Metadata(
    "topDonationCalcTimer",                             // name
    "Top Donation Calculation Time",                    // display name
    "Processing time to find the top donation",         // description
    MetricType.TIMER,                                   // type
    MetricUnits.NANOSECONDS);                           // units
Timer topDonationCalcTimer = registry.timer(topDonationCalcTimerMetadata);
public Long getTopDonation() {
 
    // Start timing here
    Timer.Context context = topDonationCalcTimer.time();
 
    Long max = 0L;
    for (Long amount : donationManager.getDonationsList()) {
        max = amount > max ? amount : max;
    }
 
    // Stop timing
    context.close();
    return max;
}
以下是 REST 端點產生的結果:
curl -k -u user:password https://localhost:9443/metrics/application/topDonationCalcTimer

# TYPE application:top_donation_calc_timer_rate_per_second gauge
application:top_donation_calc_timer_rate_per_second 0.19107302754898328
# TYPE application:top_donation_calc_timer_one_min_rate_per_second gauge
application:top_donation_calc_timer_one_min_rate_per_second 0.013233974568205872
# TYPE application:top_donation_calc_timer_five_min_rate_per_second gauge
application:top_donation_calc_timer_five_min_rate_per_second 0.4845914744130395
# TYPE application:top_donation_calc_timer_fifteen_min_rate_per_second gauge
application:top_donation_calc_timer_fifteen_min_rate_per_second 0.8866728789348088
# TYPE application:top_donation_calc_timer_mean_seconds gauge
application:top_donation_calc_timer_mean_seconds 9.37780684853573E-5
# TYPE application:top_donation_calc_timer_max_seconds gauge
application:top_donation_calc_timer_max_seconds 1.97197E-4
# TYPE application:top_donation_calc_timer_min_seconds gauge
application:top_donation_calc_timer_min_seconds 4.9630000000000004E-5
# TYPE application:top_donation_calc_timer_stddev_seconds gauge
application:top_donation_calc_timer_stddev_seconds 3.082659934664267E-5
# TYPE application:top_donation_calc_timer_seconds summary
# HELP application:top_donation_calc_timer_seconds Processing time to find the top donation
application:top_donation_calc_timer_seconds_count 63
application:top_donation_calc_timer_seconds{quantile="0.5"} 8.6069E-5
application:top_donation_calc_timer_seconds{quantile="0.75"} 1.0372E-4
application:top_donation_calc_timer_seconds{quantile="0.95"} 1.53694E-4
application:top_donation_calc_timer_seconds{quantile="0.98"} 1.96615E-4
application:top_donation_calc_timer_seconds{quantile="0.99"} 1.97197E-4
application:top_donation_calc_timer_seconds{quantile="0.999"} 1.97197E-4

curl -k -u user:password -H "Accept: application/json" https://localhost:9443/metrics/application/topDonationCalcTimer
{
   "topDonationCalcTimer": {
       "count": 63,
       "fifteenMinRate": 0.8866728789348088,
       "fiveMinRate": 0.4845914744130395,
       "max": 197197,
       "mean": 93778.06848535729,
       "meanRate": 0.19107302754898328,
       "min": 49630,
       "oneMinRate": 0.013233974568205872,
       "p50": 86069.0,
       "p75": 103720.0,
       "p95": 153694.0,
       "p98": 196615.0,
       "p99": 197197.0,
       "p999": 197197.0,
       "stddev": 30826.599346642666
   }
}

使用 CDI 來注入度量

CDI 注入提供一種簡易方法,來建立度量並登錄到 MetricRegistry。相對於建立 meta 資料物件,您可以改用 @Metric 註釋的參數,來指派度量的 meta 資料。
  • @Metric:用來說明所注入之度量的註釋。此註釋可用於「計量」、「計時器」、「計數器」和「直方圖」類型的欄位上。
  • @Metric 參數:name、displayname、units、tags、description、absolute。
    @Metric 註釋的參數類似於對應的 meta 資料欄位,只是有下列的差異。
    • tags:key=value 標籤字串的陣列
    • absolute:若為 true,則將度量名稱設為 name 參數中指定的確切名稱。若為 false,則會在套件類別和名稱中新增字首,以使用完整名稱。
    @Inject
    @Metric(name="statsHits", displayName="Stats Hits", description="Number of hits on the /stats endpoint", absolute=true)
    Counter statsHitsCounter;
    伺服器會在 MetricRegistry 中建立及登錄一個計數器,並提供給應用程式使用。計數器範例等同於 statsHitsCounter 範例。absolute=true 表示沿用所提供的名稱。未使用 units 參數,且會預設為 MetricUnits.NONE
    @Inject
    @Metric(name= "topDonationCalcTimer", unit = MetricUnits.NANOSECONDS, description="Processing time to find the top donation")
    Timer topDonationCalcTimer;
    
    @Inject
    @Metric(absolute = true, tags=["appName=DonationApp"])
    Counter counted;
下列兩個範例說明如何使用不同的參數來注入度量。

度量註釋

MicroProfile 度量也會有一些攔截程式,來處理 @Counted@Timed@Metered@Gauge 註釋。

類似於 @Metric 註釋,您可以直接使用註釋,來設定這每一項註釋的 meta 資料參數。每一項註釋都可套用至類別、方法或類型。如果沒有提供名稱,會從方法/建構子名稱取得名稱。
@Counted
此註釋會將方法、建構子或類型標示為計數器。除了 meta 資料欄位,計數器還另有一個單調欄位。如果 monotonic 設為 false,計數器會在呼叫註解方法時增量,在該註解方法返回時減量,以計數該註解方法的現行呼叫次數。如果 monotonic 設為 true,,則計數會單調增加,以計數該註解方法的呼叫總次數。
註: 依預設,monotonic 會設為 false。
@GET
@Path("/no")
@Counted(name="no", displayName="No donation count", description="Number of people that declined to donate.", monotonic=true)
public String noDonation() {
    return "Maybe next time!";
}
@Timed
此註釋會將所標註物件的建構子或方法標示為已計時。計時器度量會追蹤所標註物件的啟動頻率,以及追蹤多久才完成呼叫。
@POST
@Path("/creditcard")
@Timed(
    name="donateAmountViaCreditCard.timer",
    displayName="Donations Via Credit Cards",
    description = "Donations that were made using a credit card")
public String donateAmountViaCreditCard(@FormParam("amount") Long amount, @FormParam("card") String card) {
 
    if (processCard(card, amount))
        return "Thanks for donating!";
 
    return "Sorry, please try again.";
 
}
@Metered
此註釋會將建構子或方法標示為已計量。計量會計數建構子或方法的呼叫次數,以及追蹤它們的呼叫頻率。
@Metered(displayName="Rate of donations", description="Rate of incoming donations (the instances not the amount)")
public void addDonation(Long amount) {
    totalDonations += amount;
    donations.add(amount);
    donationDistribution.update(amount);
}
@Gauge
此註釋會將方法標示為量規。
@Gauge(
    name="donations",
    displayName="Total Donations",
    description="Total amount of money raised for charity!",
    unit = "dollars",
    absolute=true)
public Long getTotalDonations(){
    return totalDonations;
}
[18.0.0.1 以及更新版本]
重複使用度量
當您使用 MicroProfile 度量 1.1 版及更新版本時,您可以重複使用度量。@Counted@Metered@Timed 註釋具有一個旗標,來指出該度量可供其他註釋重複使用。例如,您的應用程式可能有多個端點,以用於不同的付款方法。如果要追蹤所有付款方法之間的付款次數,您可以設定 reusable=true,以使用相同的度量名稱。下列範例說明範例用法。
@POST
@Path("/creditcard")
@Counted(
    name="donation.counter",
    displayName="Number of donations",
    description = "Donations that were made using any method",
    monotonic=true,
    reusable=true)
public String donateAmountViaCreditCard(@FormParam("amount") Long amount, @FormParam("card") String card) {
 
    if (processCard(card, amount))
        return "Thanks for donating!";
 
    return "Sorry, please try again.";
 
}
@POST
@Path("/debitcard")
@Counted(
    name="donations.counter",
    displayName="Number of donations",
    description = "Donations that were made using any method",
    monotonic=true,
    reusable=true)
public String donateAmountViaDebitCard(@FormParam("amount") Long amount, @FormParam("card") String card) {
 
    if (processDebitCard(card, amount))
        return "Thanks for donating!";
 
    return "Sorry, please try again.";
 
}

CDI 提示

mpMetrics-1.0 特性需要有 CDI。只要您想注入 MetricRegistry,或是將其中一個度量類型注入至欄位,就會使用 CDI 注入。CDI 攔截程式用來將 @Timed@Counted@Metered@Gauge 度量套用至類型、建構子或方法。

使用明確的 Bean 保存檔
為了顧及應用程式的啟動效率,請確定您的 Bean 保存檔具有 beans.xml 檔。如果您正在建立 Web 應用程式,beans.xml 檔必須位於 WEB-INF 目錄。如果您正在建立 EJB 模組或 JAR 檔,beans.xml 檔必須位於 META-INF 目錄。藉由在應用程式中避開 CDI 隱含 Bean 保存檔功能,您可以調整 Liberty 伺服器,以避免在啟動時掃描 Bean 保存檔。
攔截程式 @Timed@Counted@Metered@Gauge 的限制
對於 Liberty 中以攔截程式形式實作的度量註釋,CDI 需要使用 Java 攔截程式。@Timed@Counted@Metered@Gauge 註釋是利用攔截程式來實作。您必須能夠建立一個可在程式碼路徑上啟動的 Proxy,攔截程式才能呼叫具有攔截程式註釋的建構子或方法。攔截程式的程式碼是度量的更新所在。至於哪些類型的程式碼可作為 Proxy,以及可在何處使用攔截程式註釋,則有一些限制。只有可作為 Proxy 的受管理 Bean(由 CDI 所定義), 才能與攔截程式搭配使用。Proxy 會實作成作為 Proxy 之 Bean 的子類別。為了確保類別可作為 Proxy,該類別不得標示為最終,且必須是一個非專用的 no-args 建構子。
注入限制
如果要將類型(例如 MetricRegistry)@Inject 至類別,該類別必須受 CDI 管理。下列 JAX-RS 範例說明由 CDI 自動呼叫的 someclass.setMetricsRegistry
@Path("/myPath")
public class MyService {
    // lifecyle of Someclass will be managed by CDI
    @Inject
    Someclass someclass;
 
    ...
}
 
public class Someclass {
    // Someclass was injected and is managed by CDI and therefore can use @Inject
    // setMetricRegistry will be called before the first method call to Someclass
    @Inject
    public void setMetricRegistry(final MetricRegistry metrics) {
    }
}
在下列範例中,不會呼叫類別:
@Path("/myPath")
public class MyService {
    // CDI doesn't manage lifecycle of Someclass
    Someclass someclass = new Someclass();
 
    ...
}
 
public class Someclass {
    // This method will never be called by CDI since Someclass was not created with CDI
    @Inject
    public void setMetricRegistry(MetricRegistry metrics) {
    }
}

指示主題類型的圖示 概念主題

檔名:cwlp_mp_metrics_api.html