1. 程式人生 > >騰訊王卡運營坑之一:web容器優雅停機緩慢

騰訊王卡運營坑之一: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之庖丁解牛》