SpringBoot監(jiān)控Tomcat活動線程數(shù)來判斷是否完成請求處理方式
SpringBoot監(jiān)控Tomcat活動線程數(shù)來判斷是否完成請求處理
最近項目中有一個需求,需要判斷應(yīng)用的請求是否已經(jīng)處理完畢,想了一下,打算通過定時任務(wù)定時監(jiān)控Tomcat的活動線程數(shù)來實現(xiàn)。
編碼實現(xiàn)
新建一個SpringBoot工程,添加定時任務(wù)定時監(jiān)控,代碼如下:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.embedded.tomcat.TomcatWebServer;
import org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component
public class TomcatMonitor {
? ? private Logger logger = LoggerFactory.getLogger(getClass());
? ? @Autowired
? ? ServletWebServerApplicationContext applicationContext;
? ? @Scheduled(cron = "*/1 * * * * ?")
? ? public void execute() {
? ? ? ? TomcatWebServer webServer = (TomcatWebServer) applicationContext.getWebServer();
? ? ? ? String tomcatInfo = webServer.getTomcat().getConnector().getProtocolHandler().getExecutor().toString();
? ? ? ? System.out.println("tomcatInfo: " + tomcatInfo);
? ? }
}測試
新建一個測試請求類,代碼如下:
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.TimeUnit;
@RestController
public class TestController {
? ? @RequestMapping("/test")
? ? public String test() {
? ? ? ? try {
? ? ? ? ? ? TimeUnit.SECONDS.sleep(2);
? ? ? ? } catch (InterruptedException e) {
? ? ? ? ? ? e.printStackTrace();
? ? ? ? }
? ? ? ? System.out.println("complete OK");
? ? ? ? return "OK";
? ? }
}啟動項目:
tomcatInfo: org.apache.tomcat.util.threads.ThreadPoolExecutor@49355b92[Running, pool size = 10, active threads = 0, queued tasks = 0, completed tasks = 0]
tomcatInfo: org.apache.tomcat.util.threads.ThreadPoolExecutor@49355b92[Running, pool size = 10, active threads = 0, queued tasks = 0, completed tasks = 0]
訪問測試接口,觀察Tomcat線程池變化:
tomcatInfo: org.apache.tomcat.util.threads.ThreadPoolExecutor@49355b92[Running, pool size = 10, active threads = 1, queued tasks = 0, completed tasks = 0]
complete OK
tomcatInfo: org.apache.tomcat.util.threads.ThreadPoolExecutor@49355b92[Running, pool size = 10, active threads = 0, queued tasks = 0, completed tasks = 2]
可以看到,當(dāng)有請求處理時,Tomcat的活動線程數(shù)不為0,處理完成以后回歸到0,所以可以通過活動線程數(shù)判斷當(dāng)前Tomcat是否有請求還在處理,當(dāng)然,應(yīng)用到實際業(yè)務(wù)中,還有需要完善的地方。
Tomcat線程數(shù)占滿而導(dǎo)致的線上事故
事故表現(xiàn)形式
昨天下午4點多的時候,有同學(xué)反映說,某個服務(wù)響應(yīng)時間過長,懷疑是負(fù)載均衡的問題。
排查過程
1,查看阿里云負(fù)載均衡表現(xiàn)正常,排除。
2,查看單臺服務(wù)日志出現(xiàn)OOM,可以斷定是服務(wù)出問題了。
分析原因
1,先重啟服務(wù)。
2,下載 *.hprof 日志,用MAT分析結(jié)果如下圖:
3,結(jié)果明顯指示tomcat線程池滿了,并指向了其中一個controller的外部請求。
4,結(jié)合代碼分析是請求外部HTTP連接沒有設(shè)置超時時間,導(dǎo)致大量請求堆積,tomcat線程池滿了,造成OOM。
解決辦法
1,HTTP請求使用連接池,設(shè)置超時響應(yīng)時間
2,加上監(jiān)控。


總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java Kafka實現(xiàn)優(yōu)先級隊列的示例詳解
在分布式系統(tǒng)中,消息隊列是一種常見的異步通信機制,而優(yōu)先級隊列則是消息隊列的一種特殊形式,下面我們來看看如何利用Kafka實現(xiàn)優(yōu)先級隊列吧2025-03-03
在Eclipse IDE使用Gradle構(gòu)建應(yīng)用程序(圖文)
這篇文章主要介紹了在Eclipse IDE使用Gradle構(gòu)建應(yīng)用程序(圖文),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-12-12
詳解SpringMVC學(xué)習(xí)系列(6) 之 數(shù)據(jù)驗證
這篇文章主要介紹了詳解SpringMVC學(xué)習(xí)系列(6) 之 數(shù)據(jù)驗證 ,具有一定的參考價值,感興趣的小伙伴們可以參考一下。2016-12-12
java中l(wèi)ist.forEach()和list.stream().forEach()區(qū)別
這篇文章主要介紹了java中l(wèi)ist.forEach()和list.stream().forEach()區(qū)別,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-03-03
SpringBoot切面攔截@PathVariable參數(shù)及拋出異常的全局處理方式
這篇文章主要介紹了SpringBoot切面攔截@PathVariable參數(shù)及拋出異常的全局處理方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-08-08
關(guān)于SpringBoot配置文件加載位置的優(yōu)先級
這篇文章主要介紹了關(guān)于SpringBoot配置文件加載位置的優(yōu)先級,我們也可以通過spring.config.location來改變默認(rèn)的配置文件位置,項目打包好后,我們可以通過命令行的方式在啟動時指定配置文件的位置,需要的朋友可以參考下2023-10-10

