[17.0.0.3 and later]

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 存储所有指标及其各自的元数据。指标和元数据可以使用所提供的方法来注册和检索。缺省情况下,所有与应用程序相关的指标都添加至单个作用域限定为应用程序的 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);

使用元数据描述指标

元数据是一组用于描述和总结指标相关信息的数据,它们可使查找和处理不同类型的指标更加容易。元数据粒度级别可足够详细,使得能够在属性级别定义信息。

指标元数据由下列属性组成:

元数据可更好地描述每个指标的用途。当您通过 REST API 访问指标时,此信息非常有用。
Unit
一组固定的字符串单位。
Type
标尺、计数器、计量表、直方图或计时器。
Description
人类可读的指标描述。
DisplayName
用于显示用途的人类可读的指标名称(如果指标名称并非人类可读,例如,当指标名称为 UUID 时)。
Tags
以逗号分隔的键/值(“键=值”)对的列表。
[18.0.0.1 and later]Reusable
[18.0.0.1 and later]在 MicroProfile Metrics V1.1 及更高版本中,Reusable 是一个布尔值,用于指示指标名称是否可复用。例如,如果您想要在注释多个方法时复用单个指标,可指定此属性。
通过在环境变量 MP_METRICS_TAGS 中传递以逗号分隔的“名称=值”对的列表,可以设置全局或应用程序范围的标记。
export MP_METRICS_TAGS=app=shop,node=2932

[18.0.0.1 and later]也可以使用 MicroProfile Config 来配置 MP_METRICS_TAGS。有关如何配置 MP_METRICS_TAGS 的更多信息,请参阅Liberty 运行时中的有效配置值位置

在进程生命周期中,元数据不可更改。例如,它不能在一项检索中返回秒作为单位,但在另一项检索中返回小时作为单位。
注: 如果未给出 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) 同时标记事件的多个实例。计量表提供以下信息:
  • 平均吞吐量。
  • 1/5/15 分钟指数加权的移动平均吞吐量。
  • 测量次数的计数。

可以使用计量表来计算应用程序处理的事务的速率。

以下示例使用计量表来确定调用 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) 并指定要记录的值。可使用 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);
}
将从 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 百分位数处的时间值。
  • 平均吞吐量。
  • 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;
}
将从 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 注册指标。可使用 @Metric 注释的参数对指标元数据进行赋值,而不是创建元数据对象。
  • @Metric:用于描述所注入的指标的注释。可在类型为“计量表”、“计时器”、“计数器”和“直方图”的字段中使用此注释。
  • @Metric 参数:name、displayname、units、tags、description 和 absolute。
    @Metric 注释的参数类似于相应的元数据字段,但存在下列差别。
    • tags:一系列键=值标记字符串
    • 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 注释,您可通过直接使用这些注释来设置其中每个注释的元数据参数。每个注释都可应用于类、方法或类型。如果未提供名称,可从方法/构造函数名称获取名称。
@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]
复用指标
当您使用 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) {
    }
}

用于指示主题类型的图标 概念主题

文件名:cwlp_mp_metrics_api.html