Spring Boot Actuator 监控线程池

在一个分布式系统中,一般都会有一个或多个固定的线程池来处理我们提交的任务。很多时候我们需要关注线程池的运行情况,根据情况来调整我们线程池参数。我们可以使用Actuator 很简单的完成这件事情。

pom.xml

1
2
3
4
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-actuator</artifactId>
       </dependency>

自定义一个监控线程池的 HealthContributor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
/**
 * @author lyy
 */
public class MyThreadPollHealthContributor implements HealthIndicator {
   
    private ThreadPoolExecutor executor;
   
    public MyThreadPollHealthContributor(ThreadPoolExecutor executor) {
        this.executor = executor;
    }

    @Override
    public Health health() {
        //核心线程数
        int corePoolSize = executor.getCorePoolSize();
        //活跃线程数
        int activeCount = executor.getActiveCount();
        //完成的任务数
        long completedTaskCount = executor.getCompletedTaskCount();
        //线程数历史高峰线
        int largestPoolSize = executor.getLargestPoolSize();
        //当前池中线程数
        int poolSize = executor.getPoolSize();
        BlockingQueue<Runnable> queue = executor.getQueue();
        //队列剩余空间数
        int remainingCapacity = queue.remainingCapacity();
        //队列中的任务
        int queueSize = queue.size();
        //最大线程数
        int maximumPoolSize = executor.getMaximumPoolSize();
       
        //如果当前活跃线程数 大于  80%的最大线程数,就认证是down
        double rate = BigDecimal.valueOf(activeCount)
                .divide(BigDecimal.valueOf(maximumPoolSize), 2, BigDecimal.ROUND_HALF_UP)
                .doubleValue();
       
        Map<String, Object> infoMap = new HashMap<String, Object>();
        infoMap.put("核心线程数",corePoolSize);
        infoMap.put("当前活跃线程数", activeCount);
        infoMap.put("线程峰值", largestPoolSize);
        infoMap.put("完成的任务数", completedTaskCount);
        infoMap.put("当前池中线程数", poolSize);
        infoMap.put("队列剩余大小", remainingCapacity);
        infoMap.put("当前队列中的任务数", queueSize);
        infoMap.put("设置最大线程数", maximumPoolSize);
        if(rate > 0.8) {
            return Health.down().withDetails(infoMap).build();
        }else {
            return Health.up().withDetails(infoMap).build();
        }

    }

我们可以监控多个对象,自定义一个 CompositeHealthContributor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
/**
 * @author lyy
 *  线程池
 */
public class ThreadPoolConfig {
   
    public static final ThreadPoolExecutor executor =
            new ThreadPoolExecutor(5, 10, 1, TimeUnit.MINUTES, new ArrayBlockingQueue<Runnable>(100));

}


/**
 * @author lyy
 */
@Component
public class MyCompositeHealthContributor implements CompositeHealthContributor {
   
    private Map<String, HealthContributor> map = new HashMap<String, HealthContributor>();

    @PostConstruct
    public void init() {
        MyThreadPollHealthContributor threadPool = new MyThreadPollHealthContributor(ThreadPoolConfig.executor);
        map.put("threadPoll", threadPool);
       
        addTask();
    }
   
    @Override
    public HealthContributor getContributor(String name) {
        return map.get(name);
    }

    @Override
    public Iterator<NamedContributor<HealthContributor>> iterator() {
        List<NamedContributor<HealthContributor>> contributors = new ArrayList<NamedContributor<HealthContributor>>();
       
        map.forEach((name,c) ->{
            contributors.add(NamedContributor.of(name, c));
        });
       
        return contributors.iterator();
    }
   
   
    /**
     * 模拟添加任务
     */
    public void addTask() {
        AtomicLong finishTaskNum = new AtomicLong();
        new Thread(() ->{

            while(true) {
                try {
                    ThreadPoolConfig.executor.execute(() ->{
                        try {
                            TimeUnit.SECONDS.sleep(ThreadLocalRandom.current().nextInt(10,40));
                            System.out.println("完成任务.."+ finishTaskNum.getAndIncrement());
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    });
                    TimeUnit.SECONDS.sleep(1);
                } catch (Exception e2) {
                }

               
            }
        }).start();

    }
   
}

application.yml

1
2
3
4
5
6
7
8
9
management:
  endpoints:
    web:
      exposure:
        include: "*"
       
  endpoint:
    health:
      show-details: always

ok,启动项目 访问 http://localhost:8080/actuator/health 就可以看到线程池的相关信息了