今天在公衆号中看到了一篇教你如何監控 Java 線程池運作狀态 ,才記起來我之前也做過一樣的事情,但是沒有記錄下來,今天有空記下來。我會首先介紹一下監控的背景,監控方式,執行個體代碼及效果3個部分介紹。
背景
廢話不多說,做這個監控的背景很簡單,我們的項目都是以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,效果如下,紅色框标注的便是我們添加的名額:
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZuBnL2kjM4BDMz0SMzITNwkDN1ITNx8CX1AzLchTMwIzLcNHZh9GbwV3LcRnblRnbvNWLwd3Lc52YuUXas5WYppWaopnL3d3dvw1LcpDc0RHaiojIsJye.png)
歡迎關注我的個人的部落格www.zhijianliu.cn, 虛心求教,有錯誤還請指正輕拍,謝謝
版權聲明:本文出自志健的原創文章,未經部落客允許不得轉載