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