在開發spring boot應用服務的時候,難免會使用到異步任務及執行緒池,spring boot的執行緒池是可以自定義的,所以我們經常會在專案里面看到類似于下面這樣的代碼
@Bean
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(config.getCorePoolSize());
executor.setMaxPoolSize(config.getMaxPoolSize());
executor.setQueueCapacity(config.getQueueCapacity());
executor.setThreadNamePrefix("TaskExecutePool-");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.initialize();
return executor;
}
使用起來很方便,但是這樣做有幾個問題:
- 開發人員在代碼里面隨意定義執行緒池,開發人員A自定義一個執行緒池,開發人員B自定義一個執行緒池,執行緒池的資源的使用沒有規劃與合理的安排,后期維護的成本升高,
- 一旦發現執行緒池數量不足或資源滿載,很難調整配置,只能調整代碼,重新部署,
- 多人撰寫代碼,很難統計那個服務里面存在執行緒池,有幾個執行緒池,
- 如果不去跟蹤代碼,你很難知道它的使用情況,定義了50個執行緒,存不存在資源浪費?存不存在資源等待?
為了解決上述的問題,我開發了一個Spring Boot Starter(開源專案地址:https://gitee.com/hanxt/zimug-monitor-threadpool ),方便集成到Spring Boot專案里面去,目標是:在不改變SpringBoot執行緒池的核心實作的基礎上,使其可視化、易觀測、易配置、易使用,
需要說明的是:zimug-monitor-threadpool并未改變SpringBoot執行緒池的實作,只是在其基礎上添加了初始化階段的配置自動化加載,運行時的狀態監控,所以任何有關Spring Boot執行緒池運行時性能的討論,都與本文及其實作無關,
一、易集成、易配置
通過上文的專案地址獲取原始碼,然后maven編譯install本地m2倉庫,然后通過下面的maven坐標引入
<dependency>
<groupId>com.zimug</groupId>
<artifactId>zimug-monitor-threadpool</artifactId>
<version>1.0</version>
</dependency>
如下配置spring boot YAML(application.yml)所示,配置了兩個執行緒池,分別是test、test2,當thread-pool.enable=true的時候執行緒池配置生效,
thread-pool:
enable: true
poolLists:
- poolId: test #執行緒池唯一標識
poolName: 測驗1 #執行緒池的中文描述,比如執行緒池給誰用?
coreSize: 5 #執行緒池初始化核心執行緒數量
maxSize: 10 #執行緒池最大執行緒容量
queueCapacity: 10 #執行緒池等待佇列的容量
- poolId: test2
poolName: 測驗2
coreSize: 5
maxSize: 10
queueCapacity: 10
通過下面的這張圖理解上面的配置資訊

- 當執行緒任務數量core_size被活躍任務執行緒占滿之后,執行緒任務會被放入等待佇列(queueCapacity=10)
- 當等待佇列queueCapacity也被占滿之后,才會擴大執行緒池的容量
- 執行緒池的容量最大擴展到maxSize,如果maxSize和queueCapacity都滿了,任務就阻塞了,
二、易使用
使用方式和SpringBoot 代碼方式自定義執行緒池的使用方式是一樣的,使用@Async注解的值是test,呼叫該注解標識的函式就會放入上文中配置的test執行緒池里面去執行,
@Component
public class TestTask {
@Async("test") //注意這里,test是執行緒池配置的poolId
public Future<String> test() throws Exception {
System.out.println("當前執行緒:" + Thread.currentThread().getName());
return new AsyncResult<>("測驗任務");
}
}
三、可視化易觀測
在專案中引入zimug-monitor-threadpool之后,進行執行緒池配置,使用執行緒池,訪問服務的/pool.html即可獲取當前SpringBoot服務的執行緒池配置資訊,以及運行時狀態資訊,

- 執行緒池ID、描述、初始化執行緒數、最大執行緒數、任務等待佇列的容量是上文中yaml靜態配置
- 當前執行緒池的容量,即:執行緒池當前的執行緒數量(活躍+非活躍執行緒數總和)
- 當前活躍執行緒數,即:正在運行程式任務的執行緒數量
- 執行緒池活躍執行緒的最大峰值,如果該值等于初始化執行緒數,說明曾經出現了任務等待,即:任務放入等待佇列,效率較低,如果該值大于初始化執行緒數,說明任務等待佇列曾經滿載,需要擴容,如果該值接近等于最大執行緒數,就需要擴大最大執行緒數的值,
- 當前任務等待佇列剩余的容量,剩的越少,說明正在等待執行的任務就越多,
四、實作原理
zimug-monitor-threadpool的實作原理也非常簡單,簡單說一下原理,具體實作參考原始碼,
- 首先通過SpringBoot加載yaml配置資訊,配置加載完成之后自定義實作配置自動化加載,這個實作原理及實作方法網上到處都是,我就不寫了,
- 將配置資訊加載之后new 一個ThreadPoolTaskExecutor 物件,并通過Spring的ConfigurableBeanFactory將執行緒池物件的bean注冊到Spring背景關系環境中,bean的id是poolId配置,就可以提供給運行時任務使用了,
configurableBeanFactory.registerSingleton(pool.getPoolId(), taskExecutor);
- 待需要監測執行緒池運行時狀態的時候,再把執行緒池物件通過getBean方法獲取到,從而獲取運行時資訊回傳給前臺請求,
ThreadPoolTaskExecutor memThreadPool = (ThreadPoolTaskExecutor) applicationContext.getBean(poolModel.getPoolId());
字母哥博客:zimug.com

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/501238.html
標籤:Java
下一篇:面向物件ooDay3
