一次客戶端卡崩的沉思錄

標題起的有些裝B,好吧,我認了。
今天解決客戶現場客戶端卡死問題,午飯也木吃,在此做一個總結。
問題描述
客戶向實施反饋,點擊某個功能後,客戶端就一直卡着了,直至掛掉。
相關介紹
(1)、C/S架構的ERP產品,現場產品版本較低。
(2)、最近有打業務模塊的補丁和一個基礎的補丁。
(3)、可重現。
問題分析
(1)、想到的第一步是縮小定位問題的點,具體來講是客戶端卡住了還是在服務端卡住了。
(2)、首先來判斷是不是卡在服務端了,如果是卡在服務端的話,應該有較多線程處於阻塞或者等待狀態,在此點該按鈕時,相關線程也會阻塞住。此時就需要查看服務端線程狀態了,比較方便的是,我們產品直接做了一個web項目的工具,通過訪問特定的url就可以拿到服務端線程的堆棧,通過反覆觀察服務端線程,可以排除不是卡在服務端。
(3)、接下來則是分析客戶端,通過jconsole工具進行分析現場客戶端線程,可以清晰的知道此時客戶端調到哪了,並且通過jconsole檢測並有出現死鎖,調用棧如下:

這裏寫圖片描述
說明:最上面的省略號表示省略了部分代碼,下面的省略號表示省略了重複的代碼。
根據堆棧信息,首先簡單理了一下程序執行了什麼;其次思考本地開發環境能否重現,是在哪個點執行路徑發生了改變。
顯然在本地環境並沒有重現,因此對比jconsole中的調用棧信息,在調試代碼一個方法一個方法的確認,發現如下方法在正常的情況下只執行了一次,而現場調用棧信息表示卻執行了很多次,感覺直。com.xxx.yyy.fm.common.client.DefaultTablePrecisionFormat.doAfterDataFill(DefaultTablePrecisionFormat.java:87)
最後仔細分析這個類的該方法代碼可知,當該方法內對應一行代碼調用一個方法返回的值大於0時就會進入閉環的循環調用,形成了死循環,從而最終客戶端崩掉。
因此只要在開發環境運行期做如下設置也可重現問題。
只要在第一次調入該方法時,運行期將該返回值改成大於0就會重現現場的問題-客戶端卡到最後崩掉。
總結與思考
(1)、有童鞋看完,估計會閒我笨,其實多一點想象力或經驗的話,直接分析這個線程信息就可以鎖定到是哪個類的哪個方法裏面出了問題。因爲這個調用棧可以理解爲三部分,啓動單元,重複調用單元,進行時單元,進行時單元是調用棧的最頂端,它是重複調用單元某個時點更具體在執行什麼的描述,因此可以99.99%確定是進入了死循環,99.9999%確定是程序問題而不是數據量大導致的。在分析相鄰兩個重複調用單元之間過度的方法,即可第一時間找到最可疑的點,從而分析驗證解決。
(2)、該問題可重現,直接連調現場客戶端環境,根據調用棧信息跟說不定也會更快的找出問題。
(3)、形成閉環循環調用,造成死循環代碼的場景?
以我們產品來講,進行了好幾個層次模型的抽象,在代碼上的體現是繼承體系深,因此基礎實現的一些通用功能往往都會留口子讓我們重寫,註冊監聽或者啥的,而出問題代碼要乾的事情其實就是設置業務對象表格的樣式,其中要設置的對象是A,A要設置默認格式,而默認格式的實現是該領域通用對象B實現的,B則是通過向基礎對象C(基礎模塊)註冊監聽進行回調,在回調的方法裏面又拿表格對象(基礎模塊)做了一些事,這就是一種造成較爲隱蔽死循環的場景。

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