spring Boot开发prometheus的自定义exporter组件

一、概述

目前博客上写的感觉都比较乱,很多都是基于java应用系统作埋点,并不是开发独立的exporter组件。**在开发自定义的exporter组件前,请自行了解一下什么是Spring Boot Actuator组件的作用。**简单点就是Actuator组件会将应用系统的健康检查,审计,指标收集,HTTP 跟踪等各项指标信息暴露给外部的模块(通常是接口的json格式),帮助我们监控和管理Spring Boot 应用。因为暴露内部信息的特性,Actuator 也可以和一些外部的应用监控系统整合(Prometheus, Graphite, DataDog, Influx, Wavefront, New Relic等)。这些监控系统提供了出色的仪表板,图形,分析和警报,可帮助你通过一个统一友好的界面,监视和管理你的应用程序。

二、开发思路

本篇文章的主要思路就是通过spring Boot开发Actuator组件接口转换成prometheus的对接格式。
项目整合地址:https://gitee.com/gy297879328/prometheus_demo

三、项目搭建+对接

1. 新建项目

  1. IDEA新建spring Boot项目

在这里插入图片描述

  1. 填写项目包名信息

在这里插入图片描述

  1. 选择spring web组件以及对外暴露需要的Actuator组件

在这里插入图片描述

  1. 点击完成等待项目构建。

2. 配置Actuator组件

  1. application.yml文件新增项
##项目名称
spring:
  application:
    name: dm-prometheus
##对外的端口
server:
  port: 8080

##actuator对外暴露所有的检查项
management:
  endpoints:
    web:
      exposure:
        include: '*'
  1. 启动项目

这个actuator会将一些指标暴露出来,
http://localhost:8080/actuator
在这里插入图片描述

具体的内容项看官方的图把
在这里插入图片描述

3. 对接prometheus接口

因为prometheus接口没有采用这种对接格式,需要通过转换依赖来解决格式转换问题。

  1. 引入依赖
      <dependency>
            <groupId>io.micrometer</groupId>
            <artifactId>micrometer-registry-prometheus</artifactId>
        </dependency>

2.调整配置yml文件

  • 因为本次是开发自定义exporters,不需要其他的检查项,所以通过enabled-by-default直接关闭掉其他的检查项同时只提供prometheus接口
  • 同时关闭jvm等检查项
spring:
  application:
    name: dm-prometheus
server:
  port: 8080
  
management:
  endpoints:
    # 关闭所有的检查项
    enabled-by-default: false
    web:
      exposure:
        # 暴露监控接口,*为全部接口
        include: 'prometheus'
      ## 省掉了/actuator前缀  
      base-path: "/"
  # 对外暴露prometheus接口并显示更多健康信息 
  endpoint:
    prometheus:
      enabled: true
    health:
      show-details: always
  ## 关闭内置的检查项
  metrics:
    enable:
      jvm: false
      logback: false
      files: false
      tomcat: false
      executor: false
      disk: false
      uptime: false
      integration: false
  1. 重启项目

访问接口:http://localhost:8080/prometheus
就只要这些检查项,如果不需要可以通过注入bean形式去掉这些。在Q&A中第一条过滤掉查看方法。
在这里插入图片描述

四、自定义exporter组件

组件感觉是分为两种:
第一种是:业务系统埋点,意思是调用系统controller接口以后才会将信息更新到meter上。
第二种是:每次刷新接口时,都调用一次。

1. controller接口调用

在这里插入图片描述

controller

package com.dameng.prometheus_demo.controller;


import com.dameng.prometheus_demo.services.MeterService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;


@RestController
public class MetricController {

    @Autowired
    MeterService meterService;

    @RequestMapping("/metric1")
    public String metric() {

        meterService.test();

        return "custom metric test";
    }
}

service

package com.dameng.prometheus_demo.services;


public interface MeterService {

    void test();
}

serviceImpl

package com.dameng.prometheus_demo.services;

import cn.hutool.core.util.RandomUtil;

import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Tags;
import io.prometheus.client.CollectorRegistry;
import io.prometheus.client.Gauge;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.util.concurrent.atomic.AtomicInteger;


@Service
@Component
public class MeterServiceImpl implements MeterService {

    @Autowired
    MeterRegistry meterRegistry;
    @Autowired
    private CollectorRegistry collectorRegistry;

    private AtomicInteger app_online_count;
    private Gauge my_library_transactions_active ;

    @PostConstruct
    private void init(){

        app_online_count = meterRegistry.gauge("dmdbms.temperature.gauge", Tags.of("site", "SiteA", "cab", "cab01"), new AtomicInteger(0));

        my_library_transactions_active = Gauge.build()
                .name("dmdbms_library_transactions_active")
                .help("Active transactions.")
                .labelNames("wy","zxjr","ocs","xxjf")
                .register(collectorRegistry);
    }


    public void test(){

        System.out.println("111");
        app_online_count.set(RandomUtil.randomInt(10, 100));
        
        meterRegistry.gauge("dmdbms.temperature.gauge1111", Tags.of("site", "SiteA", "cab", "cab222"), new AtomicInteger(2222));

        //传参多个值
        my_library_transactions_active.labels("site", "SiteA", "cab", "cab222").set(RandomUtil.randomInt(10, 100));
        my_library_transactions_active.labels("site", "SiteB", "cab", "cab333").set(RandomUtil.randomInt(10, 100));

    }

}

验证

先访问接口,在查看标签信息
http://localhost:8080/metric1
查看接口信息
http://localhost:8080/prometheus
在这里插入图片描述

2.MeterBinder接口

SpringBoot中提供了MeterBinder接口用于申明与注册meterRegistry。自定义Metrics只需要实现MeterBinder接口,Spring会自动发现并完成后续的杂活。

MeterBinder

增加dmdbms_system_memory_total接口

@Component
public class SystemQueryMeterBinder implements MeterBinder {


    @Autowired
    private SystemQueryService systemQueryService;

    @Override
    public void bindTo(@NonNull MeterRegistry registry) {

       //获取内存总量
       Gauge.builder("dmdbms_system_memory_total", systemQueryService, SystemQueryService::getOsCpuTotalPercentage)
                // 这个 fen 会接到 order_amount的后面及在 prometheus 中的指标名称为 order_amount_fen
                //.baseUnit("fen")
                .description("os_system_memory_total")
               // .tags("instance_name", hostName,"ip_addr",ipAddress)
                //strongReference(true) 可以将 Gauge 引用的对象设置为强引用,但不要将一些将可能被垃圾收集的对象设置强引用,将会造成不必要的内存泄露, 如果指标数据中出现 NaN,可能是 gauge 弱引用的对象被垃圾回收
                .strongReference(false)
                .register(registry);
    }
}

SystemQueryService

package com.dameng.prometheus_demo.services;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.system.oshi.OshiUtil;

import org.springframework.stereotype.Service;


@Service
public class SystemQueryService {

    public double getOsCpuTotalPercentage() {

        return RandomUtil.randomInt(10, 100);
    }
}

Q&A

1.去掉指定格式的信息

package com.dameng.prometheus_demo.config;

import io.micrometer.core.instrument.Meter;
import io.micrometer.core.instrument.config.MeterFilter;
import io.micrometer.core.instrument.config.MeterFilterReply;
import org.springframework.context.annotation.Configuration;

@Configuration
public class CustomMeterFilter implements MeterFilter {
//只需要和dmdbms相关的指标数据,其余的数据一律拒绝
    @Override
    public MeterFilterReply accept(Meter.Id id) {
        
        if (id.getName().startsWith("dmdbms")) {
            return MeterFilterReply.ACCEPT;
        }
        return MeterFilterReply.DENY;
    }
}

2.给所有标签加属性

package com.dameng.prometheus_demo.config;


import io.micrometer.core.instrument.MeterRegistry;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.actuate.autoconfigure.metrics.MeterRegistryCustomizer;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PostConstruct;

@Configuration
public class CustomMeterRegistryCustomizer implements MeterRegistryCustomizer<MeterRegistry> {
  
    @Override
    public void customize(MeterRegistry registry) {
        //默认加参数
        registry.config().commonTags("host_name", "localhost");
    }
}

3.垃圾回收与NaN

指标值注册到registry中默认为弱引用,若函数调用调用周期结束,则该值会被 Java 给标记并 GC 掉。对应的指标输出的值则会很快会变成了NaN。
在这里插入图片描述

此种短状态适用于心跳类型的指标,在预警系统中可以及时发现没有按时上报的点。
但对于相对长时间想保持住特定指标值,需要显式给到对应变量强引用。比如使用一个实例化的HashMap来 cache 相关的值。
如上段代码中,方式一为强引用,方式二则弱引用。

4. 指标设计与选型

名称 + {一组tag} + 值 为一指标形式。
如某库位温度指标形式如下:

gn_temperature_gauge_value{application="xxx",cab="cab01",site="SiteA"} 37.0

tag中值为 String 型,尽量选取『可识别』,『有限集合』的值作为 tag,比如主机,柜号,库位等等。每一个tag值都会产生一个维度,不同的 tag值+名称会被记录成不同的时间序列。
某些**很有潜力膨胀到"无限量"**的值如各种ID、邮件地址、时间戳等,就不适宜选做 tag的值
后期若使用 grafana 绘制特定指标变化图,不同时间序列也会对应到不同的多条曲线。
指标值为 double 型: 对于自定义的数值型,如温度,访问次数等的指标,原样输出即可。
若输入某些状态类的值,可定义成数值型。比如 Prometheus 中的存活状态指标 UP = 1。类似如某后台 Job 状态(pending, running, stopped),可依样定义为10,20,30。

5. 两种常用指标类型(Metric Type)

gauge: 可增可减计数器,反应某值当前一刻状态。比如称重传感器的当前重量,温度传感器的当前温度。
方式一:

Gauge.builder("gn.temperature.gauge", new AtomicInteger(37), AtomicInteger::get)

方式二:

registry.gauge("gn.temperature.gauge", Tags.of("site", "SiteA", "cab", "cab01"), new AtomicInteger(37)); 

两者等价,会输出成如下指标:

# HELP gn_temperature_gauge_value for cab temperature # TYPE gn_temperature_gauge_value gn_temperature_gauge_value{application="xxx",cab="cab01",site="SiteA",} 37.0

注意定义的名称转换:其中".“被换成了”",gauge类型的指标最后加上了"value"做结尾。指标命名只能为ASCII字母、数字、下划线和冒号,且必须配正则表达式[a-zA-Z:][a-zA-Z0-9:]*
counter:只增不减计数器,是Gauge的一个特例。适用于只有服务器重启时候才会重置的计数场景。比如"用户访问次数",某接口失败次数"等等。API 使用方式类似。

Counter counter = Counter.builder("gn.beat.counter")
  .tags("site", "SiteA", "function", "foo")
  .description("for request errors")
  .register(registry);

counter.increment();

会输出成如下指标:

# HELP gn_beat_counter_total for request errors # TYPE gn_beat_counter_total counter gn_beat_counter_total{application="xxx",function="foo",site="SiteA",} 1.0

资料

https://blog.csdn.net/aixiaoyang168/article/details/100866159
https://cloud.tencent.com/developer/article/1477642

  • 3
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值