緣由前端
最近在完善公司的基礎發佈平臺的時候,使用到了一線程去作一些異步的事情,在開發環境和測試環境驗證沒有任何問題,可是在程序在生產運行一段時間後,發現沒有獲得本身想要的結果,爲此開始了漫長的排查bug的之路,由於用到了一些線程,可是實際又沒有對這些線程足夠的監控,因此在排查問題的時候也是歷經艱難險阻;java
原始代碼web
protected ScheduledExecutorService executorService = Executors.newScheduledThreadPool(2);
/**
* 同步應用的jenkins狀態
*/
public void threadASyncAppJenkinsStatus() {
executorService.scheduleAtFixedRate(() -> {
List<ArchitectureApp> architectureApps = architectureAppMapper.listArchitectureApp();
architectureApps.parallelStream().forEach(architectureApp -> syncJenkinsBuild(architectureApp.getName(), ArchitectureType.APP));
}, 0, 6, TimeUnit.SECONDS);
}
/**
* 同步組件的jenkins狀態
*/
public void syncComponentJenkinsStatus() {
executorService.scheduleAtFixedRate(() -> {
List<ArchitectureComponent> architectureComponents = architectureComponentMapper.listArchitectureComponent();
architectureComponents.parallelStream().forEach(architectureComponent -> syncJenkinsBuild(architectureComponent.getName(), ArchitectureType.COMPONENT));
}, 0, 6, TimeUnit.SECONDS);
}
這是其中一部分的代碼,作的事情很簡單,程序每隔6s就去輪詢組件和應用的狀態,而後後面我會經過websocket同步到前端頁面。這是一段很簡單的代碼,很難想象這段代碼可能出錯。可是事與願違,經過開發和測試環境的測試,在上到生產運行了兩天發現前端頁面的jenkins狀態並無同步。而經過查看日誌,也無法觀察問題出在哪,因此只能另尋他法;緩存
ExecutorsMonitor線程監控類websocket
如下是咱們開發的一個線程池工具類,該工具類擴展ScheduledThreadPoolExecutor實現了線程池監控功能,能實時將線程池使用信息打印到日誌中,方便咱們進行問題排查、系統調優。具體代碼以下app
@Slf4j class ExecutorsMonitor extends ScheduledThreadPoolExecutor { private ConcurrentHashMap<String, Date> startTimes; private String poolName; /** * 調用父類的構造方法,並初始化HashMap和線程池名稱 * * @param corePoolSize 線程池核心線程數 * @param poolName 線程池名稱 */ public ExecutorsMonitor(int corePoolSize, String poolName) { super(corePoolSize); this.startTimes = new ConcurrentHashMap<>(); this.poolName = poolName; } /** * 線程池延遲關閉時(等待線程池裏的任務都執行完畢),統計線程池狀況 */ @Override public void shutdown() { super.shutdown(); } /** * 線程池當即關閉時,統計線程池狀況 */ @Override public List<Runnable> shutdownNow() { return super.shutdownNow(); } /** * 任務執行以前,記錄任務開始時間 */ @Override protected void beforeExecute(Thread t, Runnable r) { startTimes.put(String.valueOf(r.hashCode()), new Date()); } /** * 任務執行以後,計算任務結束時間 */ @Override protected void afterExecute(Runnable r, Throwable t) { Date startDate = startTimes.remove(String.valueOf(r.hashCode())); Date finishDate = new Date(); long diff = finishDate.getTime() - startDate.getTime(); // 統計任務耗時、初始線程數、核心線程數、正在執行的任務數量、已完成任務數量、任務總數、隊列裏緩存的任務數量、池中存在的最大線程數、最大容許的線程數、線程空閒時間、線程池是否關閉、線程池是否終止 log.info(String.format(this.poolName + "-pool-monitor: Duration: %d ms, PoolSize: %d, CorePoolSize: %d, Active: %d, Completed: %d, Task: %d, Queue: %d, LargestPoolSize: %d, MaximumPoolSize: %d, KeepAliveTime: %d, isShutdown: %s, isTerminated: %s", diff, this.getPoolSize(), this.getCorePoolSize(), this.getActiveCount(), this.getCompletedTaskCount(), this.getTaskCount(), this.getQueue().size(), this.getLargestPoolSize(), this.getMaximumPoolSize(), this.getKeepAliveTime(TimeUnit.MILLISECONDS), this.isShutdown(), this.isTerminated())); } public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize, String poolName) { return new ExecutorsMonitor(corePoolSize, poolName); } }
後來在生產終於定位問題,發現線程內部後來中止,同時發現的還有報錯,經過查閱資料發現,原來線程發生異常後會退出,經過try catch很好的解決了這個問題異步