SpringBoot-技術專區-實戰方案-應用監控線程池


背景

  廢話不多說,做這個監控的背景很簡單,我們的項目都是以spring boot框架為基礎開發的,代碼里所有的異步線程都是通過@Async標簽標注的,並且標注的時候都是指定對應線程池的,如果不知@Async標注的,可以參考@Async異步線程池用法總結, 如果你用的不是spring,就參考上文提到的公眾號文章就好。再回到背景,我們當時經常遇到的問題就是這些線程池的隊列滿了之后,新的異步任務無法添加進去的錯誤,因此我們想對所有這種類型的線程池進行監控。

監控方式

  再來介紹一下我們最終采用的方式 —— spring boot + statsd, 通過添加以下代碼就可以使statd能夠讀取/metrics接口中所有的指標並發送監控數據到statsd后端,statsd並不是本篇文章的重點,之后有時間再細講這部分的配置


@Bean
public MetricsEndpointMetricReader metricsEndpointMetricReader(final MetricsEndpoint metricsEndpoint) {
    return new MetricsEndpointMetricReader(metricsEndpoint);
}

代碼及效果

  我們所需要做的就是向/metrics接口添加線程池的指標,慶幸的是spring boot提供了良好的擴展機制,只需要實現PublicMetrics接口,實現其中的metrics方法你就能在/metrics中看到你新增的指標,上代碼:

@Component
public class AsyncThreadPoolMetrics implements PublicMetrics {
    public static final Logger LOG = LoggerFactory.getLogger(AsyncThreadPoolMetrics.class);
    private Map<String, ThreadPoolTaskExecutor> targetAsyncThreadPool;
    private static final String pattern = "async.task.%s.%s";
    @Autowired
    ApplicationContext context;
    @Override
    public Collection<Metric<?>> metrics() {
        try {
            if(targetAsyncThreadPool == null || targetAsyncThreadPool.size() == 0) {
                targetAsyncThreadPool = context.getBeansOfType(ThreadPoolTaskExecutor.class);
            }
            Collection<Metric<?>> result = new ArrayList<>();
            for (Map.Entry<String, ThreadPoolTaskExecutor> entry: targetAsyncThreadPool.entrySet()) {
                ThreadPoolTaskExecutor executor =  entry.getValue();
                result.add(new Metric<Number>(String.format(pattern, entry.getKey(), "aciveCount"), executor.getActiveCount(), new Date()));
                result.add(new Metric<Number>(String.format(pattern, entry.getKey(), "currentPoolSize"), executor.getPoolSize(), new Date()));
                result.add(new Metric<Number>(String.format(pattern, entry.getKey(), "maxPoolSize"), executor.getMaxPoolSize(), new Date()));
                result.add(new Metric<Number>(String.format(pattern, entry.getKey(), "currentSizeInQueue"),   

      executor.getThreadPoolExecutor().getQueue().size(), new Date()));
            }
            return result;
        } catch (Exception e) {
            LOG.error("Async thread pool monitor exception", e);
        }
        return Collections.emptyList();
    }
}
  從上面的代碼可以看出,我們監控的是多個線程池,原因很簡單,為了使多種類型異步任務不互相影響,我們配置了多個線程池,多個線程池有不同的名字區分。
  其實代碼很簡單,首先從容器中找到所有實現ThreadPoolTaskExecutor的bean,也就是我們要監控的線程池對象,取出正在活動的線程數,線程池的大小,配置最大可容納的線程數以及當前排隊的任務,當然還有很多其他指標,大家都可以試試,我只是選出當時我們需要的指標。當我們訪問此時應用的/metrics,效果如下,紅色框標注的便是我們添加的指標:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM