简介

Micrometer为当前比较流行的监控系统的客户端,提供了一个简单的门面,可以很方便的用它来监控你的基于JVM的应用。它的作用类似于日志系统中SLF4J!Micrometer记录的应用程序指标可以用于观察,警报和响应环境的当前/最近运行状态。
官网:https://micrometer.io/ springboot 2.0 的Spring Boot Actuator 监控应用使用的就是Micrometer

快速入门

1. maven依赖

这里我们使用prometheus作为监控系统

<dependency>        
        <groupId>io.micrometer</groupId>
        <artifactId>micrometer-registry-prometheus</artifactId>                     
        <version>1.0.6</version>
 </dependency>
2. MeterRegistry

在Micrometer中,MeterRegistry主要是用来注册meters。我们可以通过迭代MeterRegistry来拓展每个meter的度量值。最后后端会使用指标及其维度值组合,生成一个时间序列的数据。针对不同的监控系统,MeterRegistry有不同的实现类。prometheus有一个MeterRegistry的实现类prometheusRegistry。我们可以使用prometheusRegistry来将我们的应用指标暴露给Prometheus。

PrometheusMeterRegistry prometheusRegistry = new PrometheusMeterRegistry(PrometheusConfig.DEFAULT);
3. Tags

一个meter有name和tag组成。为了兼容不同的监控系统,建议命名的时候,使用小数点作为分隔符。例如

Counter counter = registry.counter("page.visitors", "age", "20s");

Tags可以是用来描述度量值的一些额外的额属性,以便我们做一些特殊的处理。在上面的代码中,page.visitors 是这个meter的名称,age=20s是它的tag。我们可以通过tag统计age在20和30之间的值。
在一些系统中,我们可以添加公共标签,例如我们的应用都有应用名称,就可以通过公共标签,给每一个metrix添加一个tag

registry.config().commonTags("application", "your applciation name");

这样我们在监控的时候就可以通过application 这个tag去监控不同的应用。

4. Counter

Counter可以用来计数。Counter接口允许您增加固定数量,但必须是正数。即Counter存的值只能一直增加。Counter可以用来记录http请求数。

Counter counter=meterRegistry.counter("page.visitors", "age", "20s");
counter.increment();

或者

Counter counter = Counter.builder("page.visitors") 
                        .tag("age", "20s")                               
                        .register(meterRegistry);
 counter.increment();

在prometheus中会存一条数据

page_visitors_total{age="20s",application="prometheus_demo",instance="192.168.0.106:8080",job="prometheus"}  1
5. Timer

Timer 可以用来测量系统中的时间的耗时和频率。Timer会记录时间发生的次数和处理的耗时。我们可以通过Timer来监控http请求耗时。

Timer timer = meterRegistry.timer("app.event");
timer.record(() -> {
            try {   
                    TimeUnit.MILLISECONDS.sleep(1500);  
           } catch (InterruptedException ignored) {
          }          
        });
timer.record(3000, TimeUnit.MILLISECONDS);

在prometheus中会存两条数据,次数和耗时总数

app_event_seconds_count{application="prometheus_demo",instance="192.168.0.106:8080",job="prometheus"} 2

app_event_seconds_sum{application="prometheus_demo",instance="192.168.0.106:8080",job="prometheus"} 4.499942169
6. Gauge

Gauge可以用来获取当前的值,一个典型的例子是,Guage可以用来获取集合、map的长度或者在与运行状态的线程的数量

List<String> list = new ArrayList<>(4);
 
Gauge gauge = Gauge
  .builder("cache.size", list, List::size)
  .register(registry);

在prometheus中会存一条数据

cache_size{application="prometheus_demo",instance="192.168.0.106:8080",job="prometheus"}	0
7. Distribution summaries

Distribution summaries用来记录事件的分布。类似于Timer,但不同的是它记录的值不是时间。例如,Distribution summaries可以用来度量请求到达服务器的负载

DistributionSummary distributionSummary = DistributionSummary
            .builder("request.size")
            .baseUnit("bytes")
            .register(meterRegistry);

distributionSummary.record(3);
distributionSummary.record(4);
distributionSummary.record(5);

在prometheus中会存三条数据

request_size_bytes_count{application="prometheus_demo",instance="192.168.0.106:8080",job="prometheus"}	3

request_size_bytes_max{application="prometheus_demo",instance="192.168.0.106:8080",job="prometheus"}	0

request_size_bytes_sum{application="prometheus_demo",instance="192.168.0.106:8080",job="prometheus"}	12
8. 抓取数据

可以调用prometheusRegistry.scrape()来抓取监控到的数据。这里我们创建一个http 服务器提供一个用来抓取数据的http请求。

try {
            HttpServer server = HttpServer.create(new InetSocketAddress(8080), 0);
            server.createContext("/prometheus", httpExchange -> {
                prometheusRegistry.counter("http.request", Tags.of("url", "/prometheus")).increment();
                String response = prometheusRegistry.scrape();
                httpExchange.sendResponseHeaders(200, response.getBytes().length);
                try (OutputStream os = httpExchange.getResponseBody()) {
                    os.write(response.getBytes());
                }
            });

            new Thread(server::start).start();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

Prometheus 配置

为了将监控数据存储到prometheus,需要在Prometheus配置一下

scrape_configs:
  # The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
  - job_name: 'prometheus'

    # metrics_path defaults to '/metrics'
    # scheme defaults to 'http'.
      
    metrics_path: /prometheus   #这个是你暴露的http请求的url
    static_configs:
    - targets: ['192.168.0.106:8080']  #http服务器的ip和端口

配好之后,就可以在prometheus查询监控数据,也可以直接访问http://ip:port/prometheus.