Spring任务

更新时间:

Spring定时任务为您在Java体系下提供了便捷的定时任务开放方式,但其便捷的同时也有很多企业化场景下的局限性。通过对接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平台上的任务执行模式为广播运行(即集群中每台机器都会在相应时点执行该任务)。如果业务需要自动在集群机器中选择一个运行,可在控制台编辑相应任务的执行模式为单机运行。关于参数的详细信息,请参见步骤三

步骤三:手动创建定时任务(可选)

说明

如果您在步骤二中配置了开启自动同步任务,则不需要手动创建定时任务。

  1. 登录分布式任务调度平台

  2. 在左侧导航栏,单击任务管理

  3. 任务管理页面,单击创建任务。选择SpringSchedule任务类型,配置对应定时任务类及其方法名。

    配置名称

    意义

    任务名

    任务名称。

    描述

    任务描述,尽量简洁地描述业务,便于后续搜索。

    应用ID

    任务所属分组。可以在下拉列表中选择。

    任务类型

    指任务所实现的语言,当前支持Java、Shell、Python、Go、HTTP、Node.js、XXL-JOB和DataWorks类型,其中Shell、Python和Go会弹出编辑框,在编辑框中编写任务脚本。

    本文任务类型为SpringSchedule。

    spring schedule配置

    定时任务代码的完整类名(class name)和任务的方法名(method name)。

    执行模式

    执行模式,这里特指任务执行的模式,当前支持以下模式。

    • 单机运行:随机选一台机器执行。

    • 广播运行:所有机器同时执行并等待全部结束。

    说明

    当选择了不同的执行模式后,高级设置中的参数会相应变化。

    优先级

    同一应用下多个任务同时在一个实例中运行时,优先级高的任务会被优先执行。但当一个应用中的多个任务在多个实例中运行时,不同优先级的任务被调度到不同实例执行,可能导致低优先级任务被优先执行。SchedulerX通过可抢占的优先级队列规避了这种可能性,并保证同时在池子中等待的高优先级任务被优先执行。更多信息,请参见可抢占的优先级队列

    任务参数

    任意字符串,可以在运行时通过上下文获取。

  4. 配置对应定时触发频率。

    说明

    频率会以控制台配置的频率为准,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表达方式。

    日历

    可选择工作日或者金融日。

  5. 设置相关报警条件和通知渠道等。关于通知渠道,请参见通知联系人和通知联系人组

    完成上述步骤后,SchedulerX 任务调度平台即可接管运行Spring的定时任务,支持为原生的Spring任务带来的各种可视化管控、任务业务日志查询、执行链路查看、任务执行通知报警等企业级能力。

步骤四:验证接入任务

  1. 启动Spring应用,启动成功之后。登录分布式任务调度平台,在左侧导航栏单击应用管理,查看如下对应应用分组的接入实例是否存在,即可说明已成功完成应用接入平台。

image

  1. 在控制台左侧导航栏单击任务管理,选择操作栏对应的应用任务单击运行一次,运行成功即可。

常见问题

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 "执行结果信息";
}