我只是同步了下Redis數據,怎麼就服務癱瘓了?

背景

bug千千萬,今天到我家。

簡要描述:數倉WEB端進行新增事件後,會注入Redis中進行緩存,供給動參服務進行響應各端SDK的請求。

下午,發現海內外redis中存儲的事件數據中僅有停用事件,而沒有啓用事件數據,以爲是個bug,然後找數據負責人、動參負責人以及產品等進行確認,對比數據及綜合意見後統一歸咎爲錯誤數據,經排查業務代碼邏輯,發現數倉WEB端業務邏輯也是按照顛倒方式進行緩存事件數據(即僅緩存停用事件,卻不緩存啓用事件),然後對代碼進行更新後,於晚上進行所有事件的緩存同步。

sync完所有事件數據到海內外redis後,同時也伴隨着3個應用的發版,動參一度癱瘓,不能響應請求。

問題排查與定位

從動參的表現來看,似乎是服務出問題了,測試發送請求,確認的確不可行後,通過jstack processId > processId.log進行查看線程狀態,發現所有線程全部BLOCKED在調用org.apache.commons.beanutils.BeanUtils.closeBean(BeanUtils.java:108)時,鎖等待在PropertyDescritor#getReadMethod(方法聲明:public synchronized Method getReadMethod())。

其次,結合今天另外兩個條件:#1,三個應用發版;#2,修復顛倒業務邏輯後,會導致緩存中數據量增多(啓用事件>停用事件等)。

所以多個小概率事件的疊加總和,導致觸發了動參服務中使用的BeanUtils#cloneBean坑點,這也是進行多次大對象copy時的服務性能嚴重下降甚至癱瘓的根源。

知道問題所在就好解決了,暫時關閉需要調用BeanUtils#cloneBean的API,同時快速進行Redis數據回退,一切恢復正常。

事後反思

其實很多工具類在多線程模式下都是多多少少存在性能或者安全問題的,比如BeanUtils#cloneBeanSimpleDateFormat等等。所以,我們需要時刻注意自己所寫代碼,也要注意成長積累,避開那些槽點,同時規避潛在的不安全點,也算是防禦性編程思想的運用吧!(此處推薦閱讀下阿里的Java開發手冊)

既然要做一名程序員,那麼請從每一行代碼做起,保證代碼質量,魔鬼總在細節中,不要最終千里之堤潰於蟻穴。各位,come on, hold on!

資料快鏈

[1] 各類對象屬性拷貝工具性能測試對比:BeanCopier、BeanUtils、DozerBeanMapper、PropertyUtils

[2] BeanUtils拷貝屬性容易忽視的坑

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