記一次Bug定位過程 —— 由非預期的頁面提示到松耦合模塊的依賴處理

背景介紹

  集羣備份:軟件爲了高可靠性,防止所在服務器down掉後業務中斷,使用了集羣,在多臺PC上跑同樣的代碼。但同一時間,只有一臺pc真正的處理業務,即Leader;其他的PC只是作爲熱備,當leader故障後纔會選舉中其中之一,處理業務。
  模塊依賴:爲支持熱插拔,使用OSGI框架,整個軟件分爲多個模塊,可在運行軟件時,不重啓整個軟件,只卸載再加載不同模塊,即實現模塊的升級。這樣實現勢必會要求模塊之間是松耦合的,模塊之間的依賴需小心處理。
  業務抽象:不能涉及到具體業務,所以本文記錄的內容會做一定抽象。當模塊加載完成(將自身註冊爲服務)後,會得到集羣事件,本服務器是否爲Leader,是才處理業務。出問題的模塊乾的事是,起個線程D,定時啓動一個固定的監控任務,並將任務的結果保存起來;同時主線程M等待隨時由用戶發起的實時監控任務。這些任務都應該有唯一的id標識,id自增是放在一個公共對象中,並統一使用一個加鎖的方法。實現時,公共對象簡化如圖(Java語言實現):
  這裏寫圖片描述

id是公共對象的一個動態成員變量,取任務id的方法如下:

public synchronized long getId(){
    this.id++;
    return this.id;    
}

問題描述

  發現保存的任務id順序不對,並不是遞增的。而是形如456,467,473,481,35,37,45,512,525,537,544,52,56,69,553,560,73,75……
  能確定的是保存id的方式是直接將新的任務id加入數組的最後,並沒有使用會將輸入重排的數據結構。

問題定位過程

  根據現象看id是沿兩條線456~560,35~75遞增的。這一點是拉出了所有的監控任務id,發現任務id兩條線的id有時會有重複,才最終確定的。在此之前,首先懷疑的是定時任務的線程D獲取到id後,在執行或者保存結果的時候,因爲什麼原因延遲,導致保存結果的時機推後了。
  發現兩條線有時重複後,就由此斷定是出現線程安全問題了。
  業務設計時,處理程序定爲唯一對象,即單例模式。自接手維護以來,這一塊的代碼就一直是單實例運行,所以最初想當然的認爲框架保證了對象唯一,即使用了單例模式。但因爲對OSGI本身不算熟悉,沒有細細的去找單例實現的地方。最終確定問題,是在處理邏輯那斷電,將對象toString打印,看到@後的數字不一樣(這裏的值時內存地址的hash值,不一樣則肯定是兩個對象),才確定對象產生了兩個。
  最終原因是OSGI啓動本模塊(active階段)時,本模塊依賴的service還沒起,創建了一個對象,使註冊依賴服務時異常。實際上依賴的模塊只是在後續使用時需要,啓動時可以不關心,即屬於弱依賴關係。
  

解決

  最終將依賴的服務的註冊時機延後到使用前,並處理此時仍沒有依賴服務的情況。實現兩模塊啓動階段的松耦合。
  
  本次的教訓就是一句話:Talking is cheap,show me the code…

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