騰訊王卡運營坑之一:web容器優雅停機緩慢
什麼叫做優雅停機:
通俗點理解(以tomcat為例),優雅停機就是當tomcat收到停機命令時,tomcat會關閉所有入口(表明我已經要停機了,你們別再來請求我了),同時對已經接受的請求繼續完成相應的處理邏輯。當所有的使用者定義的執行緒都處理完成,程序內只剩下daemon執行緒時,機徹底關機,殺死程序。
優雅停機的正確姿勢:
ps -ef|grep java 找到你需要殺死的pid
假如我們要殺死的程序pid為254569,則執行 kill 254569即可。切記千萬不能如此操作 kill -9 254569,如此操作後進程馬上殺死,同時會造成已經接收的請求不會繼續處理,從而造成一些不必要的邏輯錯誤。
問題回顧
在系統邏輯實現時,為了提高系統吞吐量,程式猿們往往會使用一些多執行緒技術,來處理一些延時高的、強依賴資源的一些請求(消耗資源比較高的通常是DBio,網路io,檔案讀寫io)。但是在使用時經常忽略了執行緒的關閉問題,下面就是騰訊王卡在運營過程中發現的問題。
在本地專案停止過程中發現,tomcat在停止時很耗時,甚至停止過程中直接丟擲異常,這個現象引起了大家的注意。
我們是這樣定位的:
第一:停止tomcat
第二:使用jstack命令檢視停機後,還有那些執行緒在執行
第三:分析打印出來的堆疊資訊
在分析jstack打印出來的堆疊資訊時,找出和專案相關程式碼,檢視可疑點。
問題分析:
該程式碼是向執行緒池提交執行一個邏輯,該邏輯是一個死迴圈
改進後的程式碼如:
改進後的方法,沒使用執行緒池提交,直接new一下thread執行即可。那問題來了,那執行緒池和直接new一個thread執行相同的方法,為啥執行緒池就不可以,new 一個thread就可以呢?
帶著問題,我們分別檢視執行緒的shutdown方法和執行緒的interrupt。相關程式碼截圖如下
我們先分析一下執行緒池的shutdown方法。
checkShutdownAccess檢查操作許可權
advanceRunState關閉執行緒池
interruptIdleWorkers中斷空閒執行緒
onShutdown取消一些延遲任務
大家請注意interruptIdleWorkers的作用中斷空閒執行緒,而我們的方法是一個死迴圈,自然不能中斷
執行緒池裡面有個方法叫shutdownNow,裡面有個方法interruptWorkers,該方法是強制關閉所有執行緒,不論是空閒還是繁忙。到此我們瞭解了執行緒池為什麼清理死迴圈的執行緒了。
而直接new一個thread之所以可以中斷一個死迴圈的執行緒,是因為interrupt0這個方法的存在,該方法作用是給執行緒打個標記(Just to set the interrupt flag),告訴作業系統,該執行緒可以終止,作業系統會自動中斷被標記中斷的執行緒。
問題解方案
最後建議大家,如果確實有業務需要寫一個死迴圈,不中斷的處理一些業務,我們建議使用如下兩種寫法
寫法一:
該方法在bean被銷燬時,start標誌會置為false,從而退出死迴圈
寫法二:
使用interrupted()方法,根據執行緒的中斷狀態來退出迴圈
總而言之,言而總之:執行緒池不能用於處理死迴圈邏輯。
如分析有誤,歡迎大家拍磚
作者也玩公眾號,歡迎關注《JAVA之庖丁解牛》