任務(wù)常見問題
本文介紹如何處理使用SchedulerX過程中的一些任務(wù)管理問題。
Spring應(yīng)用找不到Bean怎么辦?
通過應(yīng)用管理連接機器查看啟動方式,確保為Spring或者Spring Boot方式。
JobProcessor
要注入為bean
,比如加@Component
注解。排查Pom依賴如果依賴
spring-boot-devtools
則需要排除掉。如果JobProcessor和process方法有aop注解,需要升級到最新版本SchedulerX客戶端,低版本不支持aop。
因為多加了一層代理導(dǎo)致Bean類型不匹配。可以把斷點放入
DefaultListableBeanFactory
類中。其中beanDefinitionNames
成員變量是Spring注冊的Bean列表, 里面可以看到Bean被某切面代理,例如一些用戶間接引入一個錯誤的二方庫導(dǎo)致該現(xiàn)象,排除掉即可。
如果以上方案無法解決問題,可以調(diào)試ThreadContainer.start方法。如果class.forName報錯,class又確實存在,可能是業(yè)務(wù)方使用了某些框架,導(dǎo)致classLoader不一致,可以通過設(shè)置SchedulerxWorker.setClassLoader
解決。
任務(wù)失敗,報錯“Unable to make fileld private”
MapReduce用到了序列化和反序列化框架,從 Java 9 版本開始,反射私有變量需要手動開啟。請在JVM參數(shù)中添加以下配置:
--add-opens java.base/java.lang=ALL-UNNAMED
任務(wù)失敗,報錯“submit jobInstanceId to worker timeout”
當(dāng)應(yīng)用發(fā)布的時候報該問題或者偶爾報該問題時,無需處理。
如果持續(xù)報錯且每次報錯的workerAddr都是同一臺機器,說明服務(wù)端和客戶端長連接斷開,需要將該Worker節(jié)點重啟或者升級SchedulerX客戶端版本至最新版本。升級至最新版本后,斷開的長連接可自動恢復(fù)。
任務(wù)失敗,報錯“used space beyond 90.0%!”
磁盤已滿,需要清理ECS或者容器上的磁盤空間。
任務(wù)失敗,報錯“ClassNotFoundException”
說明執(zhí)行任務(wù)的Worker上沒有該類,請確保Java任務(wù)配置的Processor類名必須是類的全路徑,并非簡寫。
如果配置的jobProcessor類名正確,即為Worker上沒有該類,一般為用戶發(fā)錯包或者該應(yīng)用還連接了其他人的機器。您可以自己登錄Worker機器,通過反編譯查看詳細情況。
任務(wù)失敗,報錯“jobInstance=xxx don't update progress more than 60s”
正在運行任務(wù)的Worker停止工作或者發(fā)布時,超過60秒沒有匯報進度時,會被服務(wù)端強制終止。如果確定問題由Worker引入或者該Worker已經(jīng)不存在,則無需處理。
任務(wù)執(zhí)行失敗,且沒有錯誤信息
問題現(xiàn)象:
任務(wù)執(zhí)行失敗,且沒有錯誤信息。
可能原因:
機器或業(yè)務(wù)邏輯執(zhí)行失敗等。
解決方案:
在執(zhí)行列表頁面,選擇任務(wù)實例列表,在任務(wù)實例列表欄,選擇對應(yīng)的任務(wù),單擊操作列下的詳情,進入任務(wù)實例詳情,查看對應(yīng)執(zhí)行失敗的機器。
如果沒有子任務(wù)詳情,說明為簡單任務(wù)。查看基本信息的workAddr,該機器為執(zhí)行任務(wù)的業(yè)務(wù)機器。
登錄業(yè)務(wù)機器,打開~/logs/schedulerx/worker.log日志。
執(zhí)行
grep <實例ID> worker.log
查看該實例相關(guān)的日志。如果有ERROR級別異常,查看堆棧的具體原因。錯誤描述為空則基本為業(yè)務(wù)邏輯執(zhí)行失敗且未返回失敗信息,請先自行排查業(yè)務(wù)邏輯。
錯誤描述有框架異常,請加入釘群(釘群號:23103656)聯(lián)系SchedulerX技術(shù)支持人員。
如何排查任務(wù)失敗的原因?
如果是單機任務(wù),業(yè)務(wù)直接拋異常,可以在執(zhí)行列表頁面,單擊任務(wù)實例列表,在對應(yīng)任務(wù)實例的操作列,單擊詳情查看錯誤信息。
如果任務(wù)沒有拋異常或者使用了分布式任務(wù),您的專業(yè)版應(yīng)用可以通過日志服務(wù)來排查問題。
如果是基礎(chǔ)版應(yīng)用,您可以自行登錄Worker節(jié)點,查看SchedulerX的日志和業(yè)務(wù)自己的日志進行排查。
任務(wù)運行中卡住怎么辦?
問題現(xiàn)象:
調(diào)度任務(wù)一直處于執(zhí)行中,不能結(jié)束。
可能原因:
業(yè)務(wù)的問題。
SchedulerX的問題。
解決方案:
業(yè)務(wù)方面的問題可以按照以下方案排查,其他問題請加入釘群(釘群號:23103656)聯(lián)系SchedulerX技術(shù)支持人員。
專業(yè)版應(yīng)用:可以通過控制臺的查看堆棧功能(1.4.2及以上客戶端版本可用),來排查任務(wù)異常的堆棧。
基礎(chǔ)版應(yīng)用:可以自行登錄卡住的Worker節(jié)點,通過
jstack
命令查看堆棧,執(zhí)行命令。jstack <pid> | grep <任務(wù)實例id> -A 20
如何排查任務(wù)運行慢的原因?
開啟專業(yè)版,使用鏈路追蹤。具體操作,請參見如何接入鏈路追蹤。
任務(wù)運行實例達到上限怎么辦?
問題現(xiàn)象:
在任務(wù)管理頁面,單擊運行一次,收到任務(wù)運行實例達到上限,請稍后重試
提示。
可能原因:
該任務(wù)已經(jīng)有任務(wù)實例在運行中。
運行中的任務(wù)實例達到任務(wù)配置的最大并發(fā)數(shù)。
解決方案:
如果并發(fā)數(shù)合理,無需處理。可以在任務(wù)管理頁面,單擊 查看運行中的任務(wù)實例
如果不合理,在目標任務(wù)的操作列,單擊編輯,在高級配置里設(shè)置實例并發(fā)數(shù)。
任務(wù)上一次沒運行完,下一次是排隊還是不運行了?
任務(wù)默認并發(fā)是1,即串行跑。如果任務(wù)執(zhí)行時間比較長,上一次沒運行完,下一次調(diào)度時間到了,則下一次會直接丟棄,不會運行也不會排隊。
如果設(shè)置任務(wù)實例并發(fā)數(shù)為2,上一次沒運行完,下一次時間到了仍然可以運行一個實例,最多同時運行兩個任務(wù)實例。
如何設(shè)置一次性任務(wù)?
SchedulerX 2.0支持設(shè)置一次性任務(wù)。時間類型選擇one_time即可。一次性任務(wù)不保留任務(wù)執(zhí)行記錄。
one_time任務(wù)運行完成后怎么查看歷史記錄?
one_time任務(wù)運行完會自動銷毀,防止數(shù)據(jù)堆積,且不保留任何歷史記錄。如果需要保存歷史記錄,您可以開啟日志服務(wù),保留最近兩周所有任務(wù)的執(zhí)行日志,方便排查問題。關(guān)于如何開啟日志服務(wù),請參見應(yīng)用管理。
如何進行秒級別調(diào)度?
SchedulerX支持秒級別調(diào)度。cron、fix_rate不支持秒級別調(diào)度,您可以選擇時間類型為second_delay,即上一次運行完之后間隔幾秒再運行。
某個時間點沒有調(diào)度怎么辦?
某個單機任務(wù)有一個時間點沒有調(diào)度運行時,您需要確認機器列表是否存在機器,并確認機器是否全部處于繁忙狀態(tài)。如果不存在機器,按無可用機器或機器繁忙進行排查。更多信息,請參見無可用機器(no worker available)和機器繁忙(all workers are busy)該怎么辦?。
建議為任務(wù)配置無可用機器報警。具體操作,請參見任務(wù)管理。
SchedulerX如何設(shè)置超時時間?
SchedulerX不支持子任務(wù)級別的超時時間,只支持整個任務(wù)的超時。可以通過控制臺動態(tài)修改超時時間。具體操作,請參見任務(wù)管理。
為什么實例停止之后還會執(zhí)行?
問題現(xiàn)象:
實例停止之后仍然執(zhí)行。
可能原因:
任務(wù)實例停止后,SchedulerX會把Kill消息發(fā)送到客戶端。客戶端接收到Kill消息后,會停止下發(fā)和停止執(zhí)行未執(zhí)行的子任務(wù)、銷毀該實例的上下文、銷毀實例所有的線程池。對于已經(jīng)在執(zhí)行中的子任務(wù)不會被停止掉,只會中斷對應(yīng)的線程,所以子任務(wù)會繼續(xù)運行直到結(jié)束。
解決方案:
一般情況下,您無需處理,等待子任務(wù)執(zhí)行結(jié)束即可。
如果確實需要停止后立即結(jié)束所有運行中的任務(wù),需要修改子任務(wù)處理邏輯,增加對當(dāng)前線程Interrupt狀態(tài)的處理。
如何進行任務(wù)管理高級配置?
更多信息,請參見任務(wù)管理高級配置參數(shù)說明。
機器繁忙(all workers are busy)該怎么辦?
可以在應(yīng)用管理頁面查看實例,定位繁忙狀態(tài)的Worker,然后單擊繁忙,即可查看超過了閾值的指標。
繁忙的閾值在應(yīng)用管理頁面通過編輯應(yīng)用分組配置。
如果是load繁忙,您需要查看自己是否為容器(K8s)部署。如果為容器(K8s)部署,需要配置以下兩個參數(shù),否則采集的CPU使用率可能不準確。具體操作,請參見Spring Boot應(yīng)用接入SchedulerX。
key | 描述 | 設(shè)置值 | 起始版本 |
spring.schedulerx2.enableCgroupMetrics | 是否使用cgroup統(tǒng)計客戶端實例的指標。容器(K8s)環(huán)境需要自己手動開啟。 | true/false,默認false。 | 1.2.2.2 |
spring.schedulerx2.cgroupPathPrefix | 容器內(nèi)cgroup的路徑。 | 默認是/sys/fs/cgroup/cpu/,如果存在該路徑則不需要設(shè)置。 | 1.2.2.2 |
如何接入鏈路追蹤?
任務(wù)調(diào)度支持全鏈路追蹤。具體操作,請參見如何接入鏈路追蹤。
應(yīng)用發(fā)布過程,任務(wù)執(zhí)行卡住或變慢
問題現(xiàn)象:
應(yīng)用發(fā)布過程,任務(wù)執(zhí)行卡住或變慢。
可能原因:
對于分布式任務(wù),處理子任務(wù)的機器下線會進行重新分發(fā)并輪詢檢查機器是否在線,會導(dǎo)致整個處理過程變慢。
解決方案:
將客戶端升級至最新版本,1.7.9及以上版本該現(xiàn)象會得到優(yōu)化。
單擊運行一次后,系統(tǒng)提示輸入實例參數(shù),如何處理?
在任務(wù)管理頁面的操作列,單擊運行一次,可以執(zhí)行一次該調(diào)度任務(wù)。彈框中的實例參數(shù)非必填,主要用于測試。
單擊運行一次并輸入實例參數(shù),那么代碼中獲取的是實例參數(shù)還是任務(wù)參數(shù)?
實例參數(shù)與任務(wù)參數(shù)是兩個不同的概念,代碼中具體獲取的參數(shù)是由用戶的業(yè)務(wù)代碼決定的。
如何獲取任務(wù)參數(shù)或者實例參數(shù)?
詳細代碼如下所示。
@Component
public class JavaDemoProcessor extends JavaProcessor {
private static final Logger LOGGER = LoggerFactory.getLogger("schedulerxLog");
@Override
public ProcessResult process(JobContext jobContext) throws InterruptedException {
LOGGER.info(JSON.toJSONString(jobContext));
//獲取任務(wù)參數(shù)
String jobParameters = jobContext.getJobParameters();
//獲取實例參數(shù)
String instanceParameters = jobContext.getInstanceParameters();
LOGGER.info("任務(wù)參數(shù):" + jobParameters);
LOGGER.info("實例參數(shù)" + instanceParameters);
return new ProcessResult(InstanceStatus.SUCCESS);
}
}
如何實現(xiàn)填寫實例參數(shù)后,代碼默認獲取實例參數(shù),未填寫則獲取任務(wù)參數(shù)
詳細代碼如下所示。
@Component
public class JavaDemoProcessor extends JavaProcessor {
private static final Logger LOGGER = LoggerFactory.getLogger("schedulerxLog");
@Override
public ProcessResult process(JobContext jobContext) throws InterruptedException {
String params = null;
if (StringUtils.isNotBlank(jobContext.getInstanceParameters())) {
params = jobContext.getInstanceParameters();
} else {
params = jobContext.getJobParameters();
}
LOGGER.info("JavaDemoProcessor params:{}", params);
return new ProcessResult(InstanceStatus.SUCCESS);
}
}