在日常項目開發中,我們經常會遇到需要異步處理的任務,例如日誌服務,監控服務等。有一定開發經驗的同學首先就會想到使用線程池,因爲“在線程池中執行任務”比“爲每個任務創建一個線程”更有優勢,通過重用現有的線程,可以避免線程在不斷創建、銷燬過程中產生的開銷。在java開發中,一般做法就是基於ThreadPoolExecutor類,自定義corePoolSize, maxPoolSize, ThreadFactory等參數來創建一個線程池工具類。但是,線程池也不是沒有缺點的,對於開發者而言,我們很難在項目上線之前準確地預估業務的規模,所以,如何合理地爲線程池設置corePoolSize,maxPoolSize是一個比較難把握的事情,設置不合理會導致不能達到預計的性能,甚至會引發線上故障。不過,利用ThreadPoolExecutor爲我們提供的一些監控api,我們可以做到對線程池進行實時監控和調優。本文將利用如下幾個API,並結合Cat提供的拓展功能,實現對線程池的持續監控。
1.自定義線程池
如下代碼是我用來測試的一個線程池工具類
public class ThreadPoolManager<T> {
/**
* 根據cpu的數量動態的配置核心線程數和最大線程數
*/
//private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
/**
* 核心線程數 = CPU核心數 + 1
*/
private static final int CORE_POOL_SIZE = 20;
/**
* 線程池最大線程數 = CPU核心數 * 2 + 1
*/
private static final int MAXIMUM_POOL_SIZE = 25;
private static final int QUEUE_SIZE = 1000;
/**
* 非核心線程閒置時超時1s
*/
private static final int KEEP_ALIVE = 3;
/**
* 線程池的對象
*/
private final ThreadPoolExecutor executor = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE,
KEEP_ALIVE, TimeUnit.SECONDS, new ArrayBlockingQueue<>(QUEUE_SIZE),
Executors.defaultThreadFactory(), new DiscardPolicyWithLog());
/**
* 要確保該類只有一個實例對象,避免產生過多對象消費資源,所以採用單例模式
*/
private ThreadPoolManager() {
}
private static ThreadPoolManager sInstance;
public static ThreadPoolManager getsInstance() {
if (sInstance == null) {
synchronized (ThreadPoolManager.class) {
if (sInstance == null) {
sInstance = new ThreadPoolManager();
}
}
}
return sInstance;
}
/**
* 開啓一個無返回結果的線程
* @param r
*/
public void execute(Runnable r) {
executor.execute(r);
}
/**
* 開啓一個有返回結果的線程
*
* @param r
* @return
*/
public Future<T> submit(Callable<T> r) {
// 把一個任務丟到了線程池中
return executor.submit(r);
}
/**
* 把任務移除等待隊列
*
* @param r
*/
public void cancel(Runnable r) {
if (r != null) {
executor.getQueue().remove(r);
}
}
}
2.加入Cat監控
假設你的項目已經集成了cat監控,那麼,利用cat提供的SPI拓展能力,我們可以監控任何需要關注的系統運行時指標。我們現在想要監控線程池運行時的一些指標,幫助我們更好的優化線程池配置,提高系統性能。在以上線程池工具類的構造方法里加入如下代碼,就可以實現。
private ThreadPoolManager() {
StatusExtensionRegister.getInstance().register(new StatusExtension() {
@Override
public String getId() {
return "mqtt_msg_pool_monitor";
}
@Override
public String getDescription() {
return "mqtt消息處理線程池監控";
}
@Override
public Map<String, String> getProperties() {
Map<String,String> map = new HashMap<>();
//線程池曾經創建過的最大線程數量
map.put("largest-pool-size", String.valueOf(executor.getLargestPoolSize()));
map.put("max-pool-size", String.valueOf(executor.getMaximumPoolSize()));
map.put("core-pool-size", String.valueOf(executor.getCorePoolSize()));
map.put("current-pool-size", String.valueOf(executor.getPoolSize()));
map.put("queue-size", String.valueOf(executor.getQueue().size()));
return map;
}
});
}