Spring Boot Actuator監控的簡單使用


Spring Boot Actuator幫助我們實現了許多中間件比如mysql、es、redis、mq等中間件的健康指示器。
通過 Spring Boot 的自動配置,這些指示器會自動生效。當這些組件有問題的時候,HealthIndicator 會返回 DOWN 或 OUT_OF_SERVICE 狀態,health 端點 HTTP 響應狀態碼也會變爲 503,我們可以以此來配置程序健康狀態監控報警。
使用步驟也非常簡單,這裏演示的是線程池的監控。模擬線程池滿了狀態下將HealthInicator指示器變爲Down的狀態。

pom中引入jar


<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

引入properties配置

spring.application.name=boot


# server.servlet.context-path=/boot
# management.server.servlet.context-path=/boot
# JVM (Micrometer)要求給應用設置commonTag
management.metrics.tags.application=${spring.application.name}
#去掉重複的metrics
spring.metrics.servo.enabled=false

management.endpoint.metrics.enabled=true
management.endpoint.metrics.sensitive=false
#顯式配置不需要權限驗證對外開放的端點
management.endpoints.web.exposure.include=*
management.endpoints.jmx.exposure.include=*

management.endpoint.health.show-details=always
#Actuator 的 Web 訪問方式的根地址爲 /actuator,可以通過 management.endpoints.web.base-path 參數進行修改
management.endpoints.web.base-path=/actuator
management.metrics.export.prometheus.enabled=true

代碼

 /**
     * @Author jeffSmile
     * @Date 下午 6:10 2020/5/24 0024
     * @Description 定義一個接口,來把耗時很長的任務提交到這個 demoThreadPool 線程池,以模擬線程池隊列滿的情況
     **/

    @GetMapping("slowTask")
    public void slowTask() {
        ThreadPoolProvider.getDemoThreadPool().execute(() -> {
            try {
                TimeUnit.HOURS.sleep(1);
            } catch (InterruptedException e) {
            }
        });
    }
package com.mongo.boot.service;

import jodd.util.concurrent.ThreadFactoryBuilder;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ThreadPoolProvider {

    //一個工作線程的線程池,隊列長度10
    private static ThreadPoolExecutor demoThreadPool = new ThreadPoolExecutor(
            1, 1,
            2, TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(10),
            new ThreadFactoryBuilder().setNameFormat("demo-threadpool-%d").get());
    //核心線程數10,最大線程數50的線程池,隊列長度50
    private static ThreadPoolExecutor ioThreadPool = new ThreadPoolExecutor(
            10, 50,
            2, TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(100),
            new ThreadFactoryBuilder().setNameFormat("io-threadpool-%d").get());

    public static ThreadPoolExecutor getDemoThreadPool() {
        return demoThreadPool;
    }

    public static ThreadPoolExecutor getIOThreadPool() {
        return ioThreadPool;
    }
}
package com.mongo.boot.service;

import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * @Author jeffSmile
 * @Date 下午 6:12 2020/5/24 0024
 * @Description 自定義的 HealthIndicator 類,用於單一線程池的健康狀態
 **/

public class ThreadPoolHealthIndicator implements HealthIndicator {
    private ThreadPoolExecutor threadPool;

    public ThreadPoolHealthIndicator(ThreadPoolExecutor threadPool) {
        this.threadPool = threadPool;
    }

    @Override
    public Health health() {
        //補充信息
        Map<String, Integer> detail = new HashMap<>();
        //隊列當前元素個數
        detail.put("queue_size", threadPool.getQueue().size());
        //隊列剩餘容量
        detail.put("queue_remaining", threadPool.getQueue().remainingCapacity());

        //如果還有剩餘量則返回UP,否則返回DOWN
        if (threadPool.getQueue().remainingCapacity() > 0) {
            return Health.up().withDetails(detail).build();
        } else {
            return Health.down().withDetails(detail).build();
        }
    }
}
package com.mongo.boot.service;

import org.springframework.boot.actuate.health.CompositeHealthContributor;
import org.springframework.boot.actuate.health.HealthContributor;
import org.springframework.boot.actuate.health.NamedContributor;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/***
 * @Author jeffSmile
 * @Date 下午 6:13 2020/5/24 0024
 * @Description 定義一個 CompositeHealthContributor,來聚合兩個 ThreadPoolHealthIndicator 的實例,
 * 分別對應 ThreadPoolProvider 中定義的兩個線程池
 **/

@Component
public class ThreadPoolsHealthContributor implements CompositeHealthContributor {

    //保存所有的子HealthContributor
    private Map<String, HealthContributor> contributors = new HashMap<>();

    ThreadPoolsHealthContributor() {
        //對應ThreadPoolProvider中定義的兩個線程池
        this.contributors.put("demoThreadPool", new ThreadPoolHealthIndicator(ThreadPoolProvider.getDemoThreadPool()));
        this.contributors.put("ioThreadPool", new ThreadPoolHealthIndicator(ThreadPoolProvider.getIOThreadPool()));
    }

    @Override
    public HealthContributor getContributor(String name) {
        //根據name找到某一個HealthContributor
        return contributors.get(name);
    }

    @Override
    public Iterator<NamedContributor<HealthContributor>> iterator() {
        //返回NamedContributor的迭代器,NamedContributor也就是Contributor實例+一個命名
        return contributors.entrySet().stream()
                .map((entry) -> NamedContributor.of(entry.getKey(), entry.getValue())).iterator();
    }
}

啓動springboot驗證

這裏我訪問:http://localhost:8080/slowTask
在這裏插入圖片描述
每次訪問都向demo線程池中提交一個耗時1小時的任務,而demo線程池的核心和最大線程數都是1,隊列長度爲10,那麼當訪問11次之後,任務將被直接拒絕掉!
在這裏插入圖片描述
在這裏插入圖片描述
此時訪問:http://localhost:8080/actuator/health
在這裏插入圖片描述
demo線程池隊列已經滿了,狀態變爲DOWN。
在這裏插入圖片描述

監控內部重要組件的狀態數據

通過 Actuator 的 InfoContributor 功能,對外暴露程序內部重要組件的狀態數據!
實現一個 ThreadPoolInfoContributor 來展現線程池的信息:

package com.mongo.boot.config;

import com.mongo.boot.service.ThreadPoolProvider;
import org.springframework.boot.actuate.info.Info;
import org.springframework.boot.actuate.info.InfoContributor;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ThreadPoolExecutor;


/**
 * @Author jeffSmile
 * @Date 下午 6:37 2020/5/24 0024
 * @Description 通過 Actuator 的 InfoContributor 功能,對外暴露程序內部重要組件的狀態數據
 **/

@Component
public class ThreadPoolInfoContributor implements InfoContributor {

    private static Map threadPoolInfo(ThreadPoolExecutor threadPool) {
        Map<String, Object> info = new HashMap<>();
        info.put("poolSize", threadPool.getPoolSize());//當前池大小
        info.put("corePoolSize", threadPool.getCorePoolSize());//設置的核心池大小
        info.put("largestPoolSize", threadPool.getLargestPoolSize());//最大達到過的池大小
        info.put("maximumPoolSize", threadPool.getMaximumPoolSize());//設置的最大池大小
        info.put("completedTaskCount", threadPool.getCompletedTaskCount());//總完成任務數
        return info;
    }

    @Override
    public void contribute(Info.Builder builder) {
        builder.withDetail("demoThreadPool", threadPoolInfo(ThreadPoolProvider.getDemoThreadPool()));
        builder.withDetail("ioThreadPool", threadPoolInfo(ThreadPoolProvider.getIOThreadPool()));
    }
}

直接訪問http://localhost:8080/actuator/info
在這裏插入圖片描述

如果開啓jmx,還可以使用jconsole來查看線程池的狀態信息:

#開啓 JMX
spring.jmx.enabled=true

打開jconcole界面之後,進入MBean這個tab,可以在EndPoint下的Info操作這裏看到我們的Bean信息。
在這裏插入圖片描述
不過,除了jconsole之外,我們可以把JMX協議轉爲http協議,這裏引入jolokia:


<dependency>
    <groupId>org.jolokia</groupId>
    <artifactId>jolokia-core</artifactId>
</dependency>

重啓後訪問:http://localhost:8080/actuator/jolokia/exec/org.springframework.boot:type=Endpoint,name=Info/info
在這裏插入圖片描述

監控延伸

通過Micrometer+promethues+grafana的組合也可以進行一些生產級別的實踐。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章