今天遠程檢查醫院上線的系統運行狀況時,發現xxl-job調度系統在沒有執行任務的情況下一直報錯,如下圖:
看這發生時間,每30秒一次,特別有規律,一開始以為是有任務在執行,因為參數是json格式的字符串,封裝的時候出錯了導致轉換時報錯,但是檢查了一遍所有的任務,都不在執行的時間區間。
於是停掉了整個調度任務服務,將日志改名后進行備份,重新啟動,發現一啟動服務就開始報錯,這下可神奇了。
通過分析日志,異常發生在TriggerCallbackThread類中的238行,從gitee下載源碼進行分析:
private void retryFailCallbackFile(){
/**
* 2、找到文件來源
* 這里根據路徑拿到文件
*/
// valid
File callbackLogPath = new File(failCallbackFilePath);
if (!callbackLogPath.exists()) {
return;
}
if (callbackLogPath.isFile()) {
callbackLogPath.delete();
}
if (!(callbackLogPath.isDirectory() && callbackLogPath.list()!=null && callbackLogPath.list().length>0)) {
return;
}
// load and clear file, retry
for (File callbaclLogFile: callbackLogPath.listFiles()) {
/**
* 1、【發生異常的代碼】
* 這里是利用FileUtil文件工具類解析文件內容,那么應該可以猜測到文件的內容有問題了。
* 那他是拿哪里的文件呢
*/
byte[] callbackParamList_bytes = FileUtil.readFileContent(callbaclLogFile);
List<HandleCallbackParam> callbackParamList = (List<HandleCallbackParam>) JdkSerializeTool.deserialize(callbackParamList_bytes, List.class);
callbaclLogFile.delete();
doCallback(callbackParamList);
}
}
/**
* 3、找到文件路徑
* 這里還不能判斷是那哪個路徑下的文件,繼續分析XxlJobFileAppender.getLogPath();
*/
private static String failCallbackFilePath = XxlJobFileAppender.getLogPath().concat(File.separator).concat("callbacklog").concat(File.separator);
public class XxlJobFileAppender {
private static String logBasePath = "/data/applogs/xxl-job/jobhandler";
public static String getLogPath() {
return logBasePath;
}
}
發現拿的是/data/applogs/xxl-job/jobhandler/callbacklog/
下的日志文件,這些日志文件從哪里來的,又是干什么用的呢?
原來是執行器終止的時候,會批量獲取回調隊列里的回調入參集合,如果回調入參集合不為空就會記錄當前任務執行日志並生成日志文件。如果發生異常則會將回調入參集合序列號為byte[],然后在日志根目錄下創建callbacklog
目錄,生成回調失敗記錄文件xxl-job-callback-{x}.log
,將byte[]寫入回調失敗日志中進行保存。最后新建一個回調重試守護線程,每隔30秒執行一次。當執行器啟動時,守護線程會去讀失敗日志將byte[]轉為回調入參對象集合,如果沒問題的話就會刪除日志文件並重新執行回調方法。
所以問題就是,執行任務出現異常時將回調入參集合轉為byte[]保存到回調日志中,后面再拿到回調日志轉回byte[]的時候報錯了。
於是,我就找到這個路徑下的錯誤日志,全部delete掉,服務就正常了。