![[17.0.0.3 and later]](../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 存储所有指标及其各自的元数据。指标和元数据可以使用所提供的方法来注册和检索。缺省情况下,所有与应用程序相关的指标都添加至单个作用域限定为应用程序的 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);
使用元数据描述指标
元数据是一组用于描述和总结指标相关信息的数据,它们可使查找和处理不同类型的指标更加容易。元数据粒度级别可足够详细,使得能够在属性级别定义信息。
指标元数据由下列属性组成:
- Unit
- 一组固定的字符串单位。
- Type
- 标尺、计数器、计量表、直方图或计时器。
- Description
- 人类可读的指标描述。
- DisplayName
- 用于显示用途的人类可读的指标名称(如果指标名称并非人类可读,例如,当指标名称为 UUID 时)。
- Tags
- 以逗号分隔的键/值(“键=值”)对的列表。
Reusable
在 MicroProfile Metrics V1.1 及更高版本中,Reusable 是一个布尔值,用于指示指标名称是否可复用。例如,如果您想要在注释多个方法时复用单个指标,可指定此属性。
export MP_METRICS_TAGS=app=shop,node=2932
也可以使用 MicroProfile Config 来配置 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}
计量表
计量表用于跟踪吞吐量。
- 平均吞吐量。
- 1/5/15 分钟指数加权的移动平均吞吐量。
- 测量次数的计数。
可以使用计量表来计算应用程序处理的事务的速率。
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) 并指定要记录的值。可使用 getSnapshot() 检索直方图的当前状态(或快照)。MicroProfile Metrics 中的直方图仅支持整型或长整型值。
- 最大/最小/平均值
- 第 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 百分位数处的时间值。
- 平均吞吐量。
- 1/5/15 分钟指数加权的移动平均吞吐量。
- 所计时事件的数目的计数。
计时器用途的示例包括测量响应时间以及测量复杂逻辑的处理时间。
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 注释的参数类似于相应的元数据字段,但存在下列差别。服务器将在 MetricRegistry 中创建并注册计数器,并提供该计数器以供应用程序使用。计数器示例等效于 statsHitsCounter 示例。absolute=true 表示将按原样使用所提供的名称。未使用 units 参数,它将缺省为 MetricUnits.NONE。
- tags:一系列键=值标记字符串
- 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
- 用于将方法、构造函数或类型标记为计数器的注释。除元数据字段之外,计数器还有一个额外的 monotonic 字段。如果 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 and later]](../ng_v18001plus.gif)
- 复用指标
- 当您使用 MicroProfile Metrics V1.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 注释是使用拦截器实现的。拦截器依赖于创建代理的能力,可在代码路径中启动代理,然后它就能调用具有拦截器注释的构造函数或方法。指标在拦截器代码中进行更新。对于可代理的代码种类以及可使用拦截器注释的位置,存在一些限制。只有 CDI 定义的可代理受管 Bean 才能与拦截器配合使用。代理实现为它们所代理的 Bean 的子类。为了确保可代理某个类,这个类不得标记为 final,且必须是无自变量的非私有构造函数。
- 注入局限性
- 要通过 @Inject 将 MetricRegistry 之类的类型注入到类中,这个类必须由 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) { } }