[開源項目]可觀測、易使用的SpringBoot線程池

在開發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
字母哥博客

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章