本文介绍如何处理使用SchedulerX过程中的一些任务管理问题。
Spring应用找不到Bean怎么办?
通过应用管理连接机器查看启动方式,确保为Spring或者Spring Boot方式。
JobProcessor
要注入为bean
,比如加@Component
注解。排查Pom依赖如果依赖
spring-boot-devtools
则需要排除掉。如果JobProcessor和process方法有aop注解,需要升级到最新版本SchedulerX客户端,低版本不支持aop。
因为多加了一层代理导致Bean类型不匹配。可以把断点放入
DefaultListableBeanFactory
类中。其中beanDefinitionNames
成员变量是Spring注册的Bean列表, 里面可以看到Bean被某切面代理,例如一些用户间接引入一个错误的二方库导致该现象,排除掉即可。
如果以上方案无法解决问题,可以调试ThreadContainer.start方法。如果class.forName报错,class又确实存在,可能是业务方使用了某些框架,导致classLoader不一致,可以通过设置SchedulerxWorker.setClassLoader
解决。
任务失败,报错“Unable to make fileld private”
MapReduce用到了序列化和反序列化框架,从 Java 9 版本开始,反射私有变量需要手动开启。请在JVM参数中添加以下配置:
--add-opens java.base/java.lang=ALL-UNNAMED
任务失败,报错“submit jobInstanceId to worker timeout”
当应用发布的时候报该问题或者偶尔报该问题时,无需处理。
如果持续报错且每次报错的workerAddr都是同一台机器,说明服务端和客户端长连接断开,需要将该Worker节点重启或者升级SchedulerX客户端版本至最新版本。升级至最新版本后,断开的长连接可自动恢复。
任务失败,报错“used space beyond 90.0%!”
磁盘已满,需要清理ECS或者容器上的磁盘空间。
任务失败,报错“ClassNotFoundException”
说明执行任务的Worker上没有该类,请确保Java任务配置的Processor类名必须是类的全路径,并非简写。
如果配置的jobProcessor类名正确,即为Worker上没有该类,一般为用户发错包或者该应用还连接了其他人的机器。您可以自己登录Worker机器,通过反编译查看详细情况。
任务失败,报错“jobInstance=xxx don't update progress more than 60s”
正在运行任务的Worker停止工作或者发布时,超过60秒没有汇报进度时,会被服务端强制终止。如果确定问题由Worker引入或者该Worker已经不存在,则无需处理。
任务执行失败,且没有错误信息
问题现象:
任务执行失败,且没有错误信息。
可能原因:
机器或业务逻辑执行失败等。
解决方案:
在执行列表页面,选择任务实例列表,在任务实例列表栏,选择对应的任务,单击操作列下的详情,进入任务实例详情,查看对应执行失败的机器。
如果没有子任务详情,说明为简单任务。查看基本信息的workAddr,该机器为执行任务的业务机器。
登录业务机器,打开~/logs/schedulerx/worker.log日志。
执行
grep <实例ID> worker.log
查看该实例相关的日志。如果有ERROR级别异常,查看堆栈的具体原因。错误描述为空则基本为业务逻辑执行失败且未返回失败信息,请先自行排查业务逻辑。
错误描述有框架异常,请加入钉群(钉群号:23103656)联系SchedulerX技术支持人员。
如何排查任务失败的原因?
如果是单机任务,业务直接抛异常,可以在执行列表页面,单击任务实例列表,在对应任务实例的操作列,单击详情查看错误信息。
如果任务没有抛异常或者使用了分布式任务,您的专业版应用可以通过日志服务来排查问题。
如果是基础版应用,您可以自行登录Worker节点,查看SchedulerX的日志和业务自己的日志进行排查。
任务运行中卡住怎么办?
问题现象:
调度任务一直处于执行中,不能结束。
可能原因:
业务的问题。
SchedulerX的问题。
解决方案:
业务方面的问题可以按照以下方案排查,其他问题请加入钉群(钉群号:23103656)联系SchedulerX技术支持人员。
专业版应用:可以通过控制台的查看堆栈功能(1.4.2及以上客户端版本可用),来排查任务异常的堆栈。
基础版应用:可以自行登录卡住的Worker节点,通过
jstack
命令查看堆栈,执行命令。jstack <pid> | grep <任务实例id> -A 20
如何排查任务运行慢的原因?
开启专业版,使用链路追踪。具体操作,请参见如何接入链路追踪。
任务运行实例达到上限怎么办?
问题现象:
在任务管理页面,单击运行一次,收到任务运行实例达到上限,请稍后重试
提示。
可能原因:
该任务已经有任务实例在运行中。
运行中的任务实例达到任务配置的最大并发数。
解决方案:
如果并发数合理,无需处理。可以在任务管理页面,单击 查看运行中的任务实例
如果不合理,在目标任务的操作列,单击编辑,在高级配置里设置实例并发数。
任务上一次没运行完,下一次是排队还是不运行了?
任务默认并发是1,即串行跑。如果任务执行时间比较长,上一次没运行完,下一次调度时间到了,则下一次会直接丢弃,不会运行也不会排队。
如果设置任务实例并发数为2,上一次没运行完,下一次时间到了仍然可以运行一个实例,最多同时运行两个任务实例。
如何设置一次性任务?
SchedulerX 2.0支持设置一次性任务。时间类型选择one_time即可。一次性任务不保留任务执行记录。
one_time任务运行完成后怎么查看历史记录?
one_time任务运行完会自动销毁,防止数据堆积,且不保留任何历史记录。如果需要保存历史记录,您可以开启日志服务,保留最近两周所有任务的执行日志,方便排查问题。关于如何开启日志服务,请参见应用管理。
如何进行秒级别调度?
SchedulerX支持秒级别调度。cron、fix_rate不支持秒级别调度,您可以选择时间类型为second_delay,即上一次运行完之后间隔几秒再运行。
某个时间点没有调度怎么办?
某个单机任务有一个时间点没有调度运行时,您需要确认机器列表是否存在机器,并确认机器是否全部处于繁忙状态。如果不存在机器,按无可用机器或机器繁忙进行排查。更多信息,请参见无可用机器(no worker available)和机器繁忙(all workers are busy)该怎么办?。
建议为任务配置无可用机器报警。具体操作,请参见任务管理。
SchedulerX如何设置超时时间?
SchedulerX不支持子任务级别的超时时间,只支持整个任务的超时。可以通过控制台动态修改超时时间。具体操作,请参见任务管理。
为什么实例停止之后还会执行?
问题现象:
实例停止之后仍然执行。
可能原因:
任务实例停止后,SchedulerX会把Kill消息发送到客户端。客户端接收到Kill消息后,会停止下发和停止执行未执行的子任务、销毁该实例的上下文、销毁实例所有的线程池。对于已经在执行中的子任务不会被停止掉,只会中断对应的线程,所以子任务会继续运行直到结束。
解决方案:
一般情况下,您无需处理,等待子任务执行结束即可。
如果确实需要停止后立即结束所有运行中的任务,需要修改子任务处理逻辑,增加对当前线程Interrupt状态的处理。
如何进行任务管理高级配置?
更多信息,请参见任务管理高级配置参数说明。
机器繁忙(all workers are busy)该怎么办?
可以在应用管理页面查看实例,定位繁忙状态的Worker,然后单击繁忙,即可查看超过了阈值的指标。
繁忙的阈值在应用管理页面通过编辑应用分组配置。
如果是load繁忙,您需要查看自己是否为容器(K8s)部署。如果为容器(K8s)部署,需要配置以下两个参数,否则采集的CPU使用率可能不准确。具体操作,请参见Spring Boot应用接入SchedulerX。
key | 描述 | 设置值 | 起始版本 |
spring.schedulerx2.enableCgroupMetrics | 是否使用cgroup统计客户端实例的指标。容器(K8s)环境需要自己手动开启。 | true/false,默认false。 | 1.2.2.2 |
spring.schedulerx2.cgroupPathPrefix | 容器内cgroup的路径。 | 默认是/sys/fs/cgroup/cpu/,如果存在该路径则不需要设置。 | 1.2.2.2 |
如何接入链路追踪?
任务调度支持全链路追踪。具体操作,请参见如何接入链路追踪。
应用发布过程,任务执行卡住或变慢
问题现象:
应用发布过程,任务执行卡住或变慢。
可能原因:
对于分布式任务,处理子任务的机器下线会进行重新分发并轮询检查机器是否在线,会导致整个处理过程变慢。
解决方案:
将客户端升级至最新版本,1.7.9及以上版本该现象会得到优化。
单击运行一次后,系统提示输入实例参数,如何处理?
在任务管理页面的操作列,单击运行一次,可以执行一次该调度任务。弹框中的实例参数非必填,主要用于测试。
单击运行一次并输入实例参数,那么代码中获取的是实例参数还是任务参数?
实例参数与任务参数是两个不同的概念,代码中具体获取的参数是由用户的业务代码决定的。
如何获取任务参数或者实例参数?
详细代码如下所示。
@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));
//获取任务参数
String jobParameters = jobContext.getJobParameters();
//获取实例参数
String instanceParameters = jobContext.getInstanceParameters();
LOGGER.info("任务参数:" + jobParameters);
LOGGER.info("实例參数" + instanceParameters);
return new ProcessResult(InstanceStatus.SUCCESS);
}
}
如何实现填写实例参数后,代码默认获取实例参数,未填写则获取任务参数
详细代码如下所示。
@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);
}
}