![[17.0.0.3 以及更新版本]](../ng_v17003plus.gif)
MicroProfile 度量檢測 API
您可以使用 MicroProfile 度量 API,在應用程式中新增度量。MicroProfile 度量 API 類似於 Dropwizard 度量 API。
MicroProfile 度量的架構
- 基本
- 基本登錄用於來自伺服器(必要的話,還有來自 MicroProfile 度量規格)的度量。
- 供應商
- 供應商登錄用於產品供應商(以本例來說是 Liberty)所新增的度量。
- 應用程式
- 應用程式登錄用於您應用程式所新增的度量。應用程式登錄是預設 MetricRegistry。
mpMetrics-1.0 和 mpMetrics-1.1 特性還包含 /metrics 端點,它提供 HTTP 介面,以便與瀏覽器和監視工具互動。
檢查度量的值
MicroProfile 度量提供 /metrics 端點,您可以透過 HTTP 來存取。在使用適當的配置啟動 Liberty 伺服器之後,您可以從任何瀏覽器來檢視度量。
https://localhost:9443/metrics
一般而言,作業團隊會配置一些工具,以便監視及記錄某段時間的度量值。
新增度量至應用程式
- 直接使用 MetricRegistry。當使用此方法時,您的應用程式碼會明確建立度量,並登錄它們。
- 使用 CDI 來注入度量。當使用此方法時,CDI 會隱含建立度量,並利用應用程式 MetricRegistry 來登錄。
- 使用度量註釋。當使用此方法時,CDI 會隱含建立度量,並利用應用程式 MetricRegistry 來登錄。
直接管理 MetricRegistry
MetricRegistry 儲存了所有度量和其個別的 meta 資料。可以利用所提供的方法,來登錄及擷取度量和 meta 資料。依預設,與應用程式有關的所有度量會新增至以單一應用程式為範圍的 MetricRegistry 中。
@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 資料由下列屬性組成:
- 單位
- 一組固定的字串單位。
- 類型
- 量規、計數器、計量、直方圖或計時器。
- 說明
- 人類可讀的度量說明。
- DisplayName
- 人類可讀的顯示用度量名稱(如果該度量名稱非人所能判讀,例如,當度量名稱是 UUID 時)。
- 標籤
- 鍵值(鍵=值)配對清單,以逗點區隔。
可重複使用
在 MicroProfile 度量 1.1 版和更新版本中,可重複使用是一個布林值,指出該度量名稱可重複使用。例如,您想重複使用單一度量,而同時您又標註了多個方法。
export MP_METRICS_TAGS=app=shop,node=2932
您也可以使用 MicroProfile 配置,來配置
MP_METRICS_TAGS。如需如何配置
MP_METRICS_TAGS 的相關資訊,請參閱Liberty 執行時期中有效的配置值位置。
計數器
計數器是一種度量,用來保留增量或減量的計數。計數器的起始值會設為 0,可藉由 inc() 或 inc(long n) 來增量,以及藉由 dec() 或 dec(long n) 來減量。
您可以使用計數器,來計數所收到的要求總數,或計數同時作用中的 HTTP 階段作業總數。
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();
}
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);
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}
計量
計量用來追蹤傳輸量。
- 平均傳輸量。
- 一/五/十五分鐘的指數加權移動平均傳輸量。
- 測量數的計數。
您可以使用計量,來計算應用程式正在處理的交易速率。
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;
}
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);
}
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;
}
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 來注入度量
- @Metric:用來說明所注入之度量的註釋。此註釋可用於「計量」、「計時器」、「計數器」和「直方圖」類型的欄位上。
- @Metric 參數:name、displayname、units、tags、description、absolute。
@Metric 註釋的參數類似於對應的 meta 資料欄位,只是有下列的差異。伺服器會在 MetricRegistry 中建立及登錄一個計數器,並提供給應用程式使用。計數器範例等同於 statsHitsCounter 範例。absolute=true 表示沿用所提供的名稱。未使用 units 參數,且會預設為 MetricUnits.NONE。
- 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;
@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 註釋。
- @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 以及更新版本]](../ng_v18001plus.gif)
- 重複使用度量
- 當您使用 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) { } }