解決一個web的項目定時任務入庫“卡住”問題

事情的起因是因爲我要在一個定時任務的某處將數據入庫,一個是批量入庫,之後再是非批量的普通方式入庫,僞代碼如下:

 

for(int i=0; i<n; i++) {
    //批量入庫
pools.execute(new BatchSaveThread(list));//list爲map集合

//普通入庫
pools.execute(new SaveThread(map));

}

 這個定時任務是每隔1min就觸發的,頻率還蠻高。而且n大約是10,pools爲線程池,BatchSaveThread和SaveThread分別爲批量入庫的線程和普通入庫的線程。

 

問題現象:這個定時任務放到環境上跑,發現過個5-7min就會“卡死”,即:批量入庫或普通入庫都不能持續,普通入庫在持續最多7分鐘左右就不再進行了。debug發現普通入庫的線程會進入,但是就是沒法往下執行。

 

解決:其實可以從線程的角度考慮,之前想在定時任務當中,用線程池調配線程的方式異步地入庫,但由於批量入庫的步驟可能需要的時間較長,所以線程在此處耗費時間較多,以至於在本分鐘內沒能完成,卡在這導致普通入庫那一步也沒有完成。

把普通入庫和批量入庫分爲2個定時任務,一前一後的執行。普通入庫先執行,在那裏for循環中負責生成每次跟循環有關的list,並把他們放到內存中,之後,在晚些時候執行批量入庫,再在批量入庫的定時任務中for循環,取出每次的list,再針對該List入庫。爲進一步避免線程問題,可以擯棄異步入庫,直接取代原來的入庫線程而調用dao的相關入庫方法,,後來發現其實也並沒有消耗太多時間。

注:

1)內存存取list可以使用ServletContext的setAttribute(String key, Object obj)和getAttribute(key);取完可以removeAttribute(String key)減少內存佔用~~;key的設計可以帶上for循環裏的元素以及時間。

2)mysql中想要真正的executeBatch()起作用,要記得在jdbc的url上加上rewriteBatchedStatements=true才行,否則批量執行是不會起作用的。

改進之後,經過長時間測試發現,無論是數據量較大的批量入庫,還是普通入庫,都能平穩地運行下去了。

 

順便補充些小問題:

1.循環一個集合的前提是這個集合不爲null,雖然默認情況下是根本不用考慮的,但如果你是通過本例中ServletContext的getAttribute(String key)這種方式,或其他方式得到List——即不是通過常見的定義方式得到的List,在循環它之前,需要做容錯處理:if(list != null)再循環哦!

2.在一個定時任務類的屬性定義上不要直接帶上需要通過bean得到的其他bean類,否則會報空指針異常。比如我在一個定時任務類的的屬性上(方法外)這樣:

WebApplicationContext ctx = ContextLoader.getCurrentWebApplicationContext();

 或這樣:

Dao dao = bean.get(xxxx)

 都會報NullPointerException。之所以不能在方法體外的類屬性上直接定義這些,是因爲:定時任務類也是被配置成bean的,在Spring容器啓動成功之前,整個Spring容器的環境是還未完全搭建成功的,而bean的這些屬性會在把它管理之前就掃描到,所以你不能在還未完全搭建Spring環境成功就使用Spring的上下文。——這點需注意,可以延伸到任何一個需要被管理的bean,不能在方法體外部直接使用Spring上下文

 

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