分布式调度平台XXL-JOB

文摘   职场   2024-09-10 18:00   江苏  
  1. 背景

    一套软件,总会有各种各样的bug发生,后续需要业务补偿;又或业务需要,在某个时刻才触发逻辑,那么定时任务就理所当然的出现了。

    比如在《Java JVM对象实例个数和空间占用大小》篇章中,我们用@Scheduled来实现简单的定时任务,感兴趣的可以前往滑一下。


  2. 什么是定时任务

    定时任务是指在特定时间或时间间隔内执行的任务。在软件开发和系统运维中,定时任务经常用于自动化、批处理和数据处理等场景。

  3. 常见实现方式

    Cron表达式:一种用于调度任务的时间表达式,广泛应用于各种计划任务、定时任务等场景

    Cron表达式由6个或7个字段构成,用空格分隔,分别表示秒、分、时、日、月、周、年(可选)。以下是一个示例Cron表达式:每个字段都有特定的取值范围和支持的特殊字符。例如,"*"代表所有可能的值,"/"用于指定字段的间隔值,"?"用于日和周字段中表示不指定值等。


    // 举几个常见的例子:0 0 12 * * ?:每天中午 12 点执行。0 0/5 * * * ?:每 5 分钟执行一次。0 0 8-18 ? * MON-FRI:周一至周五的 8 点到 18 点之间每小时执行一次


    Timer类:Java标准库中提供的一种定时任务实现方式。它可以在指定时间执行任务,也可以在指定时间间隔内重复执行任务。以下是一个示例Timer类的代码:

    //delay表示延迟多少毫秒开始执行任务,period表示执行任务的时间间隔Timer timer = new Timer();timer.schedule(new TimerTask() {    public void run() {        //执行任务    }}, delay, period);


    ScheduledThreadPoolExecutorJava标准库中提供的另一种定时任务实现方式。它可以在指定时间执行任务,也可以在指定时间间隔内重复执行任务。与Timer类不同的是,ScheduledThreadPoolExecutor类使用线程池执行任务,可以避免单线程执行任务的问题。以下是一个示例ScheduledThreadPoolExecutor类的代码:

    //delay表示延迟多少毫秒开始执行任务,period表示执行任务的时间间ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1);executor.scheduleAtFixedRate(new Runnable() {    public void run() {        //执行任务    }}, delay, period, TimeUnit.MILLISECONDS);

    Quartz框架Quartz框架是一种广泛使用的定时任务框架。它提供了丰富的功能和灵活的配置选项,可以满足各种定时任务需求。Quartz框架支持基于Cron表达式和时间间隔两种方式执行任务。以下是一个示例Quartz框架的代码:

    JobDetail job = newJob(MyJob.class).withIdentity("job""group").build();Trigger trigger = newTrigger().withIdentity("trigger""group").startNow().withSchedule(simpleSchedule().withIntervalInSeconds(5).repeatForever()).build();Scheduler scheduler = new StdSchedulerFactory().getScheduler();scheduler.scheduleJob(job, trigger);scheduler.start();


    在大型的分布式业务系统中,用上述几种方式来完成Job的话,先不论优点,缺点很明显:a.控制Job执行的时间(频率)是硬编码到应用中,无法灵活调整 b.部署的多个实例会同时触发逻辑,有可能出现bug c.多实例资源无法均衡使用 。。。what shall we do?


  4. XXL_JOB

    项目地址:https://github.com/xuxueli/xxl-job/tree/master



    调度中心(xxl-job-admin):统一管理任务调度平台上调度任务,负责触发调度执行,并且提供任务管理平台。该模块可以参考:https://www.xuxueli.com/xxl-job/#源码仓库地址,部署比较简单,按照步骤走就行:执行DDL脚本、编译、启动。



    客户端集成:

    <!--maven依赖--><!-- http://repo1.maven.org/maven2/com/xuxueli/xxl-job-core/ --><dependency>    <groupId>com.xuxueli</groupId>    <artifactId>xxl-job-core</artifactId>    <version>${最新稳定版本}</version></dependency>
    #配置添加xpilot.tools.xxl.job.executor.port = 1${server.port}xpilot.tools.xxl.job.executor.ip = xpilot.tools.xxl.job.executor.address = xpilot.tools.xxl.job.executor.log-retention-days = 30xpilot.tools.xxl.job.executor.log-path = /data/applogs/xxl-job/${spring.application.name}xpilot.tools.xxl.job.executor.app-name = api-job-executor# xxl_job admin addrxpilot.tools.xxl.job.admin.addresses = http://ip:port/xxl-job-adminxpilot.tools.xxl.job.enable = truexpilot.tools.xxl.job.accessToken = 8ko6S2JmcHgS
    //执行器配置@Slf4j@Configuration@ConditionalOnProperty(value = "xpilot.tools.xxl.job.enable", havingValue = "true")public class XxlJobConfig {    @Value("${xpilot.tools.xxl.job.admin.addresses}")    private String adminAddresses;
    @Value("${xpilot.tools.xxl.job.accessToken}") private String accessToken;
    @Value("${xpilot.tools.xxl.job.executor.app-name}") private String appname;
    @Value("${xpilot.tools.xxl.job.executor.address}") private String address;
    @Value("${xpilot.tools.xxl.job.executor.ip}") private String ip;
    @Value("${xpilot.tools.xxl.job.executor.port}") private int port;
    @Value("${xpilot.tools.xxl.job.executor.log-path}") private String logPath;
    @Value("${xpilot.tools.xxl.job.executor.log-retention-days}") private int logRetentionDays;
    @Bean public XxlJobSpringExecutor xxlJobExecutor() { log.info(">>>>>>>>>>> xxl-job config init...."); XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor(); xxlJobSpringExecutor.setAdminAddresses(adminAddresses); xxlJobSpringExecutor.setAppname(appname); xxlJobSpringExecutor.setAddress(address); xxlJobSpringExecutor.setIp(ip); xxlJobSpringExecutor.setPort(port); xxlJobSpringExecutor.setAccessToken(accessToken); xxlJobSpringExecutor.setLogPath(logPath); xxlJobSpringExecutor.setLogRetentionDays(logRetentionDays); return xxlJobSpringExecutor; }}


    Job定义:使用@XxlJob 注解定义一个任务,交给调度中心管理。

    @Componentpublic class SyncVideoJob {    @XxlJob("checkJobRunStateJob")    public void checkJobRunStateJob() {        XxlJobHelper.log("checkJobRunStateJob start...");        String param = XxlJobHelper.getJobParam();        XxlJobHelper.log("checkJobRunStateJob config job param:{}", param);        StopWatch watch = new StopWatch();        watch.start("checkJobRunStateJob");        //your biz        watch.stop();        XxlJobHelper.log("checkJobRunStateJob end...");    }}

  5. 调度配置
    打开任务调度中心,默认登录账号“admin/123456”,按照上述配置中的xxl.job.executor.app-name指定的名字,配置执行器和任务管理。



  6. 后期

  7. 上述步骤完成后,如果有配置报警邮件,剩下的多数就是每日关注下有没有邮件、任务执行的日志是什么,都可以通过登录平台查看,多多使用自然就会了。


    关于传递的参数可能使用以下代码获取:

     String param = XxlJobHelper.getJobParam(); //字符串转换为对象 方便运行时传递给业务private SyncVideoReq initConfig(String param) {    return JacksonUtil.parse(param, SyncVideoReq.class);}

    学如逆水行舟,不进则退。

    ——《增广贤文》


晚霞程序员
一位需要不断学习的30+程序员……