Spring Boot如何自定义监控指标

本文涉及的产品
可观测监控 Prometheus 版,每月50GB免费额度
简介: Spring Boot如何自定义监控指标

1.创建项目

pom.xml引入相关依赖

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.olive</groupId>
  <artifactId>prometheus-meter-demo</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.3.7.RELEASE</version>
    <relativePath />
  </parent>
  <properties>
    <java.version>1.8</java.version>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <spring-boot.version>2.3.7.RELEASE</spring-boot.version>
  </properties>
  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <!-- Micrometer Prometheus registry  -->
    <dependency>
      <groupId>io.micrometer</groupId>
      <artifactId>micrometer-registry-prometheus</artifactId>
    </dependency>
  </dependencies>
  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-dependencies</artifactId>
        <version>${spring-boot.version}</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>
</project>

2.自定义指标

  • 方式一

直接使用micrometer核心包的类进行指标定义和注册

package com.olive.monitor;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.DistributionSummary;
import io.micrometer.core.instrument.MeterRegistry;
@Component
public class NativeMetricsMontior {
  /**
   * 支付次数
   */
  private Counter payCount;
  /**
   * 支付金额统计
   */
  private DistributionSummary payAmountSum;
  @Autowired
  private MeterRegistry registry;
  @PostConstruct
  private void init() {
    payCount = registry.counter("pay_request_count", "payCount", "pay-count");
    payAmountSum = registry.summary("pay_amount_sum", "payAmountSum", "pay-amount-sum");
  }
  public Counter getPayCount() {
    return payCount;
  }
  public DistributionSummary getPayAmountSum() {
    return payAmountSum;
  }
}
  • 方式二

通过引入micrometer-registry-prometheus包,该包结合prometheus,对micrometer进行了封装

<dependency>
      <groupId>io.micrometer</groupId>
      <artifactId>micrometer-registry-prometheus</artifactId>
    </dependency>

同样定义两个metrics

package com.olive.monitor;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import io.prometheus.client.CollectorRegistry;
import io.prometheus.client.Counter;
@Component
public class PrometheusMetricsMonitor {
  /**
   * 订单发起次数
   */
  private Counter orderCount;
  /**
   * 金额统计
   */
  private Counter orderAmountSum;
  @Autowired
  private CollectorRegistry registry;
  @PostConstruct
  private void init() {
    orderCount = Counter.build().name("order_request_count")
        .help("order request count.")
        .labelNames("orderCount")
        .register();
    orderAmountSum = Counter.build().name("order_amount_sum")
        .help("order amount sum.")
        .labelNames("orderAmountSum")
        .register();
    registry.register(orderCount);
    registry.register(orderAmountSum);
  }
  public Counter getOrderCount() {
    return orderCount;
  }
  public Counter getOrderAmountSum() {
    return orderAmountSum;
  }
}

prometheus 4种常用Metrics

Counter

连续增加不会减少的计数器,可以用于记录只增不减的类型,例如:网站访问人数,系统运行时间等。

对于Counter类型的指标,只包含一个inc()的方法,就是用于计数器+1.

一般而言,Counter类型的metric指标在冥冥中我们使用_total结束,如http_requests_total.

Gauge

可增可减的仪表盘,曲线图

对于这类可增可减的指标,用于反应应用的当前状态。

例如在监控主机时,主机当前空闲的内存大小,可用内存大小等等。

对于Gauge指标的对象则包含两个主要的方法inc()和dec(),用于增加和减少计数。

Histogram

主要用来统计数据的分布情况,这是一种特殊的metrics数据类型,代表的是一种近似的百分比估算数值,统计所有离散的指标数据在各个取值区段内的次数。例如:我们想统计一段时间内http请求响应小于0.005秒、小于0.01秒、小于0.025秒的数据分布情况。那么使用Histogram采集每一次http请求的时间,同时设置bucket。

Summary

Summary和Histogram非常相似,都可以统计事件发生的次数或者大小,以及其分布情况,他们都提供了对时间的计数_count以及值的汇总_sum,也都提供了可以计算统计样本分布情况的功能,不同之处在于Histogram可以通过histogram_quantile函数在服务器计算分位数。而Sumamry的分位数则是直接在客户端进行定义的。因此对于分位数的计算,Summary在通过PromQL进行查询的时候有更好的性能表现,而Histogram则会消耗更多的资源,但是相对于客户端而言Histogram消耗的资源就更少。用哪个都行,根据实际场景自由调整即可。

3. 测试

定义两个controller分别使用NativeMetricsMontiorPrometheusMetricsMonitor

package com.olive.controller;
import java.util.Random;
import javax.annotation.Resource;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.olive.monitor.NativeMetricsMontior;
@RestController
public class PayController {
  @Resource
  private NativeMetricsMontior monitor;
  @RequestMapping("/pay")
  public String pay(@RequestParam("amount") Double amount) throws Exception {
    // 统计支付次数
    monitor.getPayCount().increment();
    Random random = new Random();
    //int amount = random.nextInt(100);
    if(amount==null) {
      amount = 0.0;
    }
    // 统计支付总金额
    monitor.getPayAmountSum().record(amount);
    return "支付成功, 支付金额: " + amount;
  }
}
package com.olive.controller;
import java.util.Random;
import javax.annotation.Resource;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.olive.monitor.PrometheusMetricsMonitor;
@RestController
public class OrderController {
  @Resource
  private PrometheusMetricsMonitor monitor;
  @RequestMapping("/order")
  public String order(@RequestParam("amount") Double amount) throws Exception {
    // 订单总数
    monitor.getOrderCount()
      .labels("orderCount")
      .inc();
    Random random = new Random();
    //int amount = random.nextInt(100);
    if(amount==null) {
      amount = 0.0;
    }
    // 统计订单总金额
    monitor.getOrderAmountSum()
      .labels("orderAmountSum")
      .inc(amount);
    return "下单成功, 订单金额: " + amount;
  }
}

启动服务

访问http://127.0.0.1:9595/actuator/prometheus;正常看到监测数据

改变amount多次方式http://127.0.0.1:8080/order?amount=100http://127.0.0.1:8080/pay?amount=10后;再访问http://127.0.0.1:9595/actuator/prometheus。查看监控数据

4.项目中的应用

项目中按照上面说的方式进行数据埋点监控不太现实;在spring项目中基本通过AOP进行埋点监测。比如写一个切面Aspect;这样的方式就非常友好。能在入口就做了数据埋点监测,无须在controller里进行代码编写。

package com.olive.aspect;
import java.time.LocalDate;
import java.util.concurrent.TimeUnit;
import javax.servlet.http.HttpServletRequest;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import io.micrometer.core.instrument.Metrics;
@Aspect
@Component
public class PrometheusMetricsAspect {
// 切入所有controller包下的请求方法
@Pointcut("execution(* com.olive.controller..*.*(..))")
public void controllerPointcut() {
    }
@Around("controllerPointcut()")
public Object MetricsCollector(ProceedingJoinPoint joinPoint) throws Throwable {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String userId = StringUtils.hasText(request.getParameter("userId")) ? 
            request.getParameter("userId") : "no userId";
// 获取api url
String api = request.getServletPath();
// 获取请求方法
String method = request.getMethod();
long startTs = System.currentTimeMillis();
LocalDate now = LocalDate.now();
        String[] tags = new String[10];
        tags[0] = "api";
        tags[1] = api;
        tags[2] = "method";
        tags[3] = method;
        tags[4] = "day";
        tags[5] = now.toString();
        tags[6] = "userId";
        tags[7] = userId;
String amount = StringUtils.hasText(request.getParameter("amount")) ? 
            request.getParameter("amount") : "0.0";
        tags[8] = "amount";
        tags[9] = amount;
// 请求次数加1
//自定义的指标名称:custom_http_request_all,指标包含数据
        Metrics.counter("custom_http_request_all", tags).increment();
Object object = null;
try {
            object = joinPoint.proceed();
        } catch (Exception e) {
//请求失败次数加1
            Metrics.counter("custom_http_request_error", tags).increment();
throw e;
        } finally {
long endTs = System.currentTimeMillis() - startTs;
//记录请求响应时间
           Metrics.timer("custom_http_request_time", tags).record(endTs, TimeUnit.MILLISECONDS);
        }
return object;
    }
}

编写好切面后,重启服务;访问controller的接口,同样可以进行自定义监控指标埋点

相关实践学习
容器服务Serverless版ACK Serverless 快速入门:在线魔方应用部署和监控
通过本实验,您将了解到容器服务Serverless版ACK Serverless 的基本产品能力,即可以实现快速部署一个在线魔方应用,并借助阿里云容器服务成熟的产品生态,实现在线应用的企业级监控,提升应用稳定性。
相关文章
|
4月前
|
监控 Java 数据库连接
Spring Boot中的健康检查和监控
Spring Boot中的健康检查和监控
|
1月前
|
监控 Java 对象存储
监控与追踪:如何利用Spring Cloud Sleuth和Netflix OSS工具进行微服务调试
监控与追踪:如何利用Spring Cloud Sleuth和Netflix OSS工具进行微服务调试
43 1
|
3月前
|
Java 数据安全/隐私保护 Spring
揭秘Spring Boot自定义注解的魔法:三个实用场景让你的代码更加优雅高效
揭秘Spring Boot自定义注解的魔法:三个实用场景让你的代码更加优雅高效
|
3月前
|
JSON 安全 Java
|
3月前
|
Java Spring 监控
Spring Boot Actuator:守护你的应用心跳,让监控变得触手可及!
【8月更文挑战第31天】Spring Boot Actuator 是 Spring Boot 框架的核心模块之一,提供了生产就绪的特性,用于监控和管理 Spring Boot 应用程序。通过 Actuator,开发者可以轻松访问应用内部状态、执行健康检查、收集度量指标等。启用 Actuator 需在 `pom.xml` 中添加 `spring-boot-starter-actuator` 依赖,并通过配置文件调整端点暴露和安全性。Actuator 还支持与外部监控工具(如 Prometheus)集成,实现全面的应用性能监控。正确配置 Actuator 可显著提升应用的稳定性和安全性。
127 0
|
3月前
|
监控 安全 Java
【开发者必备】Spring Boot中自定义注解与处理器的神奇魔力:一键解锁代码新高度!
【8月更文挑战第29天】本文介绍如何在Spring Boot中利用自定义注解与处理器增强应用功能。通过定义如`@CustomProcessor`注解并结合`BeanPostProcessor`实现特定逻辑处理,如业务逻辑封装、配置管理及元数据分析等,从而提升代码整洁度与可维护性。文章详细展示了从注解定义、处理器编写到实际应用的具体步骤,并提供了实战案例,帮助开发者更好地理解和运用这一强大特性,以实现代码的高效组织与优化。
179 0
|
4月前
|
监控 druid Java
spring boot 集成配置阿里 Druid监控配置
spring boot 集成配置阿里 Druid监控配置
290 6
|
4月前
|
消息中间件 Java Kafka
Spring boot 自定义kafkaTemplate的bean实例进行生产消息和发送消息
Spring boot 自定义kafkaTemplate的bean实例进行生产消息和发送消息
187 5
|
3月前
|
Java Spring 容器
【Azure Spring Cloud】在Azure Spring Apps上看见 App Memory Usage 和 jvm.menory.use 的指标的疑问及OOM
【Azure Spring Cloud】在Azure Spring Apps上看见 App Memory Usage 和 jvm.menory.use 的指标的疑问及OOM
|
4月前
|
Java Spring 容器
Spring boot 自定义ThreadPoolTaskExecutor 线程池并进行异步操作
Spring boot 自定义ThreadPoolTaskExecutor 线程池并进行异步操作
211 3