Spring定时任务为您在Java体系下提供了便捷的定时任务开放方式,但其便捷的同时也有很多企业化场景下的局限性。通过对接SchedulerX任务调度,可快速实现企业化运用的支持。
前提条件
客户端1.8.13及以上版本
采用Spring Boot模式接入SchedulerX平台具体操作,请参见Spring Boot应用接入SchedulerX。
接入指南
步骤一:添加pom依赖
以Spring Boot接入模式为例,应用程序的pom.xml文件中添加依赖及启动类。
schedulerx2.version
使用客户端最新版本。更多信息,请参见客户端发布记录。
<dependency>
<groupId>com.aliyun.schedulerx</groupId>
<artifactId>schedulerx2-spring-boot-starter</artifactId>
<version>${schedulerx2.version}</version>
<!--如果用的是logback,需要把log4j和log4j2排掉 -->
<exclusions>
<exclusion>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
</exclusion>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
</exclusions>
</dependency>
无论是已经使用Spring定时任务或初次使用,都需要在启用类上保持@EnableScheduling
注解开启。如下所示:
@SpringBootApplication
@EnableScheduling /** 开启Spring定时任务 */
public class SchedulerXWorkerApplication {
public static void main(String[] args) {
SpringApplication.run(SchedulerXWorkerApplication.class, args);
}
}
/** Spring原生配置的定时任务类*/
@Service
public class SpringScheduledProcessor {
@Scheduled(cron = "0/2 * * * * ?")
public void hello() {
logger.info(DateUtil.now() + " hello world. start");
logger.info(DateUtil.now() + " hello world. end");
}
}
对于新接入上述配置或已经满足上述配置的用户,SchedulerX 默认不会主动接管业务应用中原本的Spring定时任务,相应定时任务还是会由Spring容器进行调度,不影响原本已有的Spring定时任务运行。
步骤二:添加配置参数
后续业务需要让SchedulerX任务调度平台来接管Spring定时任务的运行,则可以在Properties文件中添加如下配置。
# 1、应用接入配置
spring.schedulerx2.endpoint=${endpoint}
spring.schedulerx2.namespace=${namespace}
spring.schedulerx2.groupId=${groupId}
spring.schedulerx2.appKey=${appKey}
# 2、启用SchedulerX接管Spring定时任务
spring.schedulerx2.task.scheduling.scheduler=schedulerx
# 3、SchedulerX接管Spring定时任务,开启自动同步任务;以下非必选参数
#spring.schedulerx2.task.scheduling.sync=true
#spring.schedulerx2.regionId=同步至目标区域编号(关于RegionId请参见服务接入点)
#spring.schedulerx2.aliyunAccessKey=XXXXXXXXX
#spring.schedulerx2.aliyunSecretKey=XXXXXXXXX
配置说明:
应用接入配置:登录分布式任务调度平台,在左侧导航栏,单击应用管理,在应用管理页面下的操作栏选择已创建的应用,单击接入配置即可获得接入配置信息,如果是首次新接入需要创建一个应用分组。
开启自动同步任务配置:对于原本已使用Spring定时任务且存量定时任务较多的用户,可以选择在应用配置文件中开启自动同步任务,大幅度简化步骤三中的手动创建过程。配置中的RegionId请参见服务接入点中的地域ID。
自动同步的任务为了保持与原生Spring任务在集群环境中运行的规则一致,默认同步至SchedulerX平台上的任务执行模式为广播运行(即集群中每台机器都会在相应时点执行该任务)。如果业务需要自动在集群机器中选择一个运行,可在控制台编辑相应任务的执行模式为单机运行。关于参数的详细信息,请参见步骤三。
步骤三:手动创建定时任务(可选)
如果您在步骤二中配置了开启自动同步任务,则不需要手动创建定时任务。
登录分布式任务调度平台。
在左侧导航栏,单击任务管理。
在任务管理页面,单击创建任务。选择SpringSchedule任务类型,配置对应定时任务类及其方法名。
配置名称
意义
任务名
任务名称。
描述
任务描述,尽量简洁地描述业务,便于后续搜索。
应用ID
任务所属分组。可以在下拉列表中选择。
任务类型
指任务所实现的语言,当前支持Java、Shell、Python、Go、HTTP、Node.js、XXL-JOB和DataWorks类型,其中Shell、Python和Go会弹出编辑框,在编辑框中编写任务脚本。
本文任务类型为SpringSchedule。
spring schedule配置
定时任务代码的完整类名(class name)和任务的方法名(method name)。
执行模式
执行模式,这里特指任务执行的模式,当前支持以下模式。
单机运行:随机选一台机器执行。
广播运行:所有机器同时执行并等待全部结束。
说明当选择了不同的执行模式后,高级设置中的参数会相应变化。
优先级
同一应用下多个任务同时在一个实例中运行时,优先级高的任务会被优先执行。但当一个应用中的多个任务在多个实例中运行时,不同优先级的任务被调度到不同实例执行,可能导致低优先级任务被优先执行。SchedulerX通过可抢占的优先级队列规避了这种可能性,并保证同时在池子中等待的高优先级任务被优先执行。更多信息,请参见可抢占的优先级队列。
任务参数
任意字符串,可以在运行时通过上下文获取。
配置对应定时触发频率。
说明频率会以控制台配置的频率为准,Spring定时任务代码中原生注解
@Scheduled
中配置将会失效,但该注解在代码中需要保留。定时参数说明如下:
配置名称
意义
时间类型
none:无调度方式,一般通过工作流触发。
cron:Cron表达式。
api:通过API触发。
fixed_rate:固定频率。
second_delay:秒级固定延迟。
one_time:一次性任务。
cron表达式(仅适用于cron时间类型)
填写Cron表达式。可以直接按照Cron语法填写,也可以使用工具生成并验证。
固定频率(仅适用于fixed_rate时间类型)
填写固定频率,单位为秒,只支持60秒以上。例如200表示每200s调度一次。
固定延迟(仅适用于second_delay时间类型)
填写固定延迟,单位为秒。范围为1秒~60秒。例如5表示延迟5秒触发调度。
高级配置参数说明如下:
配置名称
意义
数据时间偏移
数据时间相对于调度时间的偏移,可以在调度时从上下文获取该值。
时区
可以根据实际情况选择不同时区,包括一些常用国家或地区,也包括标准的GMT表达方式。
日历
可选择工作日或者金融日。
设置相关报警条件和通知渠道等。关于通知渠道,请参见通知联系人和通知联系人组。
完成上述步骤后,SchedulerX 任务调度平台即可接管运行Spring的定时任务,支持为原生的Spring任务带来的各种可视化管控、任务业务日志查询、执行链路查看、任务执行通知报警等企业级能力。
步骤四:验证接入任务
启动Spring应用,启动成功之后。登录分布式任务调度平台,在左侧导航栏单击应用管理,查看如下对应应用分组的接入实例是否存在,即可说明已成功完成应用接入平台。
在控制台左侧导航栏单击任务管理,选择操作栏对应的应用任务单击运行一次,运行成功即可。
常见问题
SchedulerX接管后原Spring定时器依旧运行
由于应用中配置了自定义的Scheduler调度器导致SchedulerX覆盖自定义处理器。请排查业务应用工程中是否存在实现org.springframework.scheduling.annotation.SchedulingConfigurer
接口的类,确认是否调用了ScheduledTaskRegistrar的setScheduler方法覆盖默认调度器,如果存在,请注释掉相关逻辑即可。
Spring任务如何获取任务上下文
在业务应用工程代码中增加以下代码获取任务上下文。
JobContext jobContext = ContainerFactory.getContainerPool().getContext();
Spring任务是否支持返回结果
版本客户端大于1.10.11时,Spring任务支持返回结果,您可以直接在定时方法上返回任意结果。
@Scheduled(cron = "0/5 * * * * ?")
public ProcessResult helloStandalone1() {
try {
logger.info(DateUtil.now() + " " + Thread.currentThread().getName() + " hello world. start");
TimeUnit.SECONDS.sleep(2L);
logger.info(DateUtil.now() + " " + Thread.currentThread().getName() + " hello world. end");
} catch (Exception e) {
e.printStackTrace();
logger.info(DateUtil.now() + " " + Thread.currentThread().getName() + " hello world. exception end..");
}
return new ProcessResult(true, "执行结果信息");
}
@Scheduled(cron = "0/5 * * * * ?")
public String helloStandalone2() {
try {
logger.info(DateUtil.now() + " " + Thread.currentThread().getName() + " hello world. start");
TimeUnit.SECONDS.sleep(2L);
logger.info(DateUtil.now() + " " + Thread.currentThread().getName() + " hello world. end");
} catch (Exception e) {
e.printStackTrace();
logger.info(DateUtil.now() + " " + Thread.currentThread().getName() + " hello world. exception end..");
}
return "执行结果信息";
}