定时任务:Timer,@Scheduled,Quartz,xxl-job

记录几种Java定时任务玩法

一.Timer

1.需要关注两个类,Timer和TimerTask
    TimerTask:任务内容
    Timer:里面有调度TimerTask执行的方法
2.适用于比较简单的计时任务(简单任务下或许比Quartz用起来更方便,不用引入额外的包,逻辑也没有那么复杂)
3.两个类均位于java.util包下
4.如果出现错误,程序会直接崩溃,而不会捕获异常进行异常处理
5.代码
package com.shixin.pawcode.resources.timer;
/**
* @Description
* @Author shixin
* @Date 2021/5/12 8:51
*/
public class MyTimer {
    private static int count = 0;
    public static void main(String[] args) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        //延迟2s开始执行,执行间隔1s
        System.out.println("当前时间 : "+sdf.format(new Date()));
        new Timer().schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println(count++ + " : " +sdf.format(new Date()));
            }
        }, 2000L, 1000L);
    }
}

执行结果

 
 
除了上面的调度方法外,在Timer里面还重载了好多种方法,可以自行查阅相关文档或者反编译看源码注释学习
 
 
 

二.@Scheduled

这个注解是Spring自带的,直接按照步骤使用即可。

1.在启动类上添加注解@EnableScheduling

2.代码

package com.shixin.pawcode.resources.config;
@Slf4j
@Component
public class SpringAnnotationScheduler {
    @Scheduled(cron = "* * * * * ?")
    private void test(){
        log.info("定时任务测试");
    }
}

这样就行了......不懂参数cron的可以看看这篇文章 :cron详解,在文章下半部分的代码部分也有介绍cron,可以继续往下看。另外附上生成器网址:Cron表达式生成

三.Quartz  

英 [kwɔːts]   美 [kwɔːrts]

1.Quartz是什么

Quartz是一个完全由java编写的开源作业调度框架。不要让作业调度这个术语吓着你。尽管Quartz框架整合了许多额外功能, 但就其简易形式看,你会发现它易用得简直让人受不了!
————根据我的使用经验,确实是相当的简单,而且逻辑思路也非常的清晰

2.Quartz能做什么

假设你有以下需求,或许可以使用它:
--想要每天凌晨1点统计昨天新增的用户
--想要每隔一小时备份自己数据库的数据
--想要在特定的节日提醒自己做某些事情
--想要代替人工,每半个小时自动处理邮件
--定期清理数据库不需要的数据
在需要做重复功的时候就可以考虑使用任务调度机制了

3.如何使用

(1)需要使用外部jar包,先引入相关包,我用的版本是2.3.2
<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz</artifactId>
    <version>${quartz.version}</version>
</dependency>

(2)定义一个类,实现Job接口。

需要重写Job接口的execute。这里需要注意的是JobExecutionContext这个参数,通过它可以获得所有运行相关的参数或者是专门传过来的值,在方法内,主要写需要执行的逻辑,至于怎么执行由其它地方规定。
package com.shixin.pawcode.resources.quartz;
/**
* @Description
* @Author shixin
* @Date 2021/5/12 13:35
*/
public class TestJob implements Job {
    private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    @Getter
    @Setter
    private String str;
    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        //编写具体的业务逻辑
        System.out.println("当前时间:"+sdf.format(new Date()));
        //获取JobDataMap  方法一
        JobDataMap jobDetailMap = jobExecutionContext.getJobDetail().getJobDataMap();
        for (String key : jobDetailMap.keySet()){
            System.out.println(key + " : " + jobDetailMap.get(key));
        }
        //获取JobDataMap 方法二
        System.out.println(str);
    }
}

(3)三种核心要素

 
JobDetail:任务 , Trigger:触发器 , Scheduler:调度器
要实现定时任务,首先要先创建一个 任务(JobDetail),在任务中需要描述执行哪个任务(上面的Job),也可以规定这个任务是名称以及所属的组,或者一些其他的参数。
有了任务之后我们就需要准备好 触发器(Trigger),规定我们需要如何去执行任务,如循环间隔?何时开始?何时结束?等等都可以规定。比较常用的有SimpleTrigger和CronTrigger,后者用的比较多,有一套Cron语法。
表达式生成地址:https://cron.qqe2.com/
 
任务与触发器就好比螺丝与螺母的关系,他们的型号都一样,但是用的地方不一样。A1螺丝可以对得上B1螺母,B2螺母,B3螺母。B1螺母同样适合A1螺丝,A2螺丝.....。他们之间可以相互适配使用。
当光有螺丝和螺母不行,还得有螺丝刀- 调度器(Scheduler ,他的作用主要是将任务与触发器绑定。
package com.shixin.pawcode.resources.quartz;
/**
* @Description
* @Author shixin
* @Date 2021/5/12 13:38
*/
public class TestScheduler {
    private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    public static void main(String[] args) throws SchedulerException {
        //任务:创建一个JobDetail实例,将该实例与Job绑定 : 任务的描述
        JobDataMap detailMap = new JobDataMap();
        detailMap.put("str-Map","map-JobDetail");
        detailMap.put("int-Map",1);
        JobDetail jobDetail = JobBuilder.newJob(TestJob.class)
                .withIdentity("jobDetailName","jobDetailGroup")
                .usingJobData("str","JobDetail")
                .usingJobData("int",-1)
                .usingJobData(detailMap)
                .build();

        //触发器:创建一个Trigger实例 :定义如何执行,什么时候触发,什么时候结束
        Calendar startCalendar = Calendar.getInstance();
        startCalendar.add(Calendar.SECOND,3);
        Calendar endCalendar = Calendar.getInstance();
        endCalendar.add(Calendar.SECOND,7);
        /*SimpleTrigger trigger = (SimpleTrigger)TriggerBuilder.newTrigger()
                .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                        //执行时间间隔
                        .withIntervalInSeconds(2)
                        //永远重复执行 ,这个和执行次数选一个
                        //.repeatForever()
                        //执行次数
                        .withRepeatCount(2))
                .withIdentity("triggerName","triggerGroup")
                //被调度后马上执行,和具体执行时间选一个
                //.startNow()
                //被调度后执行的具体的时间
                .startAt(startCalendar.getTime())
                //被调度后执行的结束的时间,会覆盖重复次数的效果
                .endAt(endCalendar.getTime())
                .build();*/

        /*
        * 基于日历的作业调度器,更加强大。重点是Cron表达式掌握
        * Cron表达式由7个子表达式组成的字符串,描述了时间的详细信息

        [秒] [分] [时] [日] [月] [周] [年]
        , 或者
        - 之间
        * 每
        / 每 三个/人  1分钟/小时
        ? 不关心

        * 举例:  
        * 1.每天的10:15分触发  [ 0 15 10 ? * * ]
        * 2.每天下午的2:00 - 2:55开始(每隔五分钟触发一次)   [ 0 0/5 14 * * ? ]
        * 3.从周一到周五每天上午的10:15触发   [ 0 15 10 ? * MON-FRI ]
        * 4.每月的第三周的星期五的10:15开始触发  [ 0 15 10 ? * 6#3 ]
        * 5.从2020-2021年每月最后一周的星期五的10点15分触发  [ 0 15 10 ? * 6L 2020-2021 ]
        * */
        CronTrigger trigger = (CronTrigger) TriggerBuilder.newTrigger()
                .withSchedule(CronScheduleBuilder.cronSchedule("* * * * * ? *"))
                .withIdentity("triggerName","triggerGroup")
                .startNow()
                .build();

        //调度器:创建Scheduler实例,并执行,将jobDetail和trigger绑定。
        SchedulerFactory  schedulerFactory = new StdSchedulerFactory();
        Scheduler scheduler = schedulerFactory.getScheduler();
        scheduler.start();
        System.out.println(sdf.format(new Date()));
        scheduler.scheduleJob(jobDetail,trigger);
    }
}

四.Quzrtz与SpringBoot整合

1.导入依赖包
      
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-quartz</artifactId>
        </dependency>
2.编写任务
package com.shixin.pawcode.resources.quartz.job;
public class Job1 extends QuartzJobBean{
    @Override
    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
       System.out.println(context.getJobDetail().getKey().getName() + ": 任务正在执行!");
    }
}
package com.shixin.pawcode.resources.quartz.job;
public class Job2 extends QuartzJobBean{
    @Override
    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
       System.out.println(context.getJobDetail().getKey().getName() + ": 任务正在执行!");
    }
}

3.编写触发器和调度器

实现了CommandLineRunner接口之后,在程序启动的时候就会触发run函数内的任务执行了。整合好后可以开始启动程序了
package com.shixin.pawcode.resources.config;

import com.shixin.pawcode.resources.quartz.job.Job1;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.stereotype.Component;

/**
* @Description 定时任务配置
* @Author shixin
* @Date 2021/5/13 15:28
*/
@Component
public class CronSchedulerJob implements CommandLineRunner{

    @Autowired
    private SchedulerFactoryBean schedulerFactoryBean;

    @Override
    public void run(String... args) throws Exception {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        scheduler.start();
        job1(scheduler);
        job2(scheduler);
    }
    private void job1(Scheduler scheduler) throws SchedulerException{
        JobDetail jobDetail = JobBuilder.newJob(Job1.class) .withIdentity("job1", "group1").build();
        CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule("0/2 * * * * ?");
        CronTrigger cronTrigger = TriggerBuilder.newTrigger()
                                                .withIdentity("trigger1", "group1")
                                                .usingJobData("name","shixin")
                                                .withSchedule(scheduleBuilder).build();
        scheduler.scheduleJob(jobDetail, cronTrigger);
    }


    private void job2(Scheduler scheduler) throws SchedulerException{
        JobDetail jobDetail = JobBuilder.newJob(Job1.class) .withIdentity("job2", "group2").build();
        CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule("0/3 * * * * ?");
        CronTrigger cronTrigger = TriggerBuilder.newTrigger()
                                                .withIdentity("trigger2", "group2")
                                                .usingJobData("name","shixin")
                                                .withSchedule(scheduleBuilder).build();
        scheduler.scheduleJob(jobDetail, cronTrigger);
    }
}

五.xxl-job调度中心的使用以及与SpringBoot整合

[中文文档] / [English Documentation] / [源码仓库地址]

1.一些使用心得

这个分布式调度平台的使用有些类似于注册中心的使用,在注册中心中,应用可以自动通过心跳连接注册中心,注册中心对应用有一定的控制权限。在xxl-job中,有两个重要的"组件",调度中心与执行器,他们的关系就好比注册中心与应用一样。

2.通过源码仓库地址下载源码,如果有时间,建议将doc里面的XXL-JOB官方文档或者阅读上面的[文档],里面前几个步骤可以走完一个完整的流程。

3..按照官方文档的介绍或者下面的配置修改xxl-job-admin的application.properties,然后打包

     ### 调度中心JDBC链接:链接地址请保持和 2.1章节 所创建的调度数据库的地址一致
    spring.datasource.url=jdbc:mysql://127.0.0.1:3306/xxl_job?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai
    spring.datasource.username=root
    spring.datasource.password=root_pwd
    spring.datasource.driver-class-name=com.mysql.jdbc.Driver
    ### 报警邮箱
    spring.mail.host=smtp.qq.com
    spring.mail.port=25
    spring.mail.username=xxx@qq.com
    spring.mail.password=xxx
    spring.mail.properties.mail.smtp.auth=true
    spring.mail.properties.mail.smtp.starttls.enable=true
    spring.mail.properties.mail.smtp.starttls.required=true
    spring.mail.properties.mail.smtp.socketFactory.class=javax.net.ssl.SSLSocketFactory
    ### 调度中心通讯TOKEN [选填]:非空时启用;
    xxl.job.accessToken=
    ### 调度中心国际化配置 [必填]: 默认为 "zh_CN"/中文简体, 可选范围为 "zh_CN"/中文简体, "zh_TC"/中文繁体 and "en"/英文;
    xxl.job.i18n=zh_CN
    ## 调度线程池最大线程配置【必填】
    xxl.job.triggerpool.fast.max=200
    xxl.job.triggerpool.slow.max=100
    ### 调度中心日志表数据保存天数 [必填]:过期日志自动清理;限制大于等于7时生效,否则, 如-1,关闭自动清理功能;
    xxl.job.logretentiondays=30

4.根据上面配置的数据库,去里面执行doc/db/tables_xxl_job.sql文件

5.运行打包的文件,访问:http://localhost:8080/xxl-job-admin

6.这个时候就已经配置好调度中心了,但是因为还没有配置执行器,所以无法成功执行任务

7.在你要使用定时任务的项目中引入以下jar包以及添加并且修改配置

<dependency>
    <groupId>com.xuxueli</groupId>
    <artifactId>xxl-job-core</artifactId>
    <version>${最新稳定版本}</version>
</dependency>
     ### 调度中心部署跟地址 [选填]:如调度中心集群部署存在多个地址则用逗号分隔。执行器将会使用该地址进行"执行器心跳注册"和"任务结果回调";为空则关闭自动注册;
    ###这里需要填写调度器的地址
    xxl.job.admin.addresses=http://127.0.0.1:8080/xxl-job-admin
    
    ### 执行器通讯TOKEN [选填]:非空时启用;
    xxl.job.accessToken=
    
    ### 执行器AppName [选填]:执行器心跳注册分组依据;为空则关闭自动注册
    xxl.job.executor.appname=xxl-job-executor-sample
    ### 执行器注册 [选填]:优先使用该配置作为注册地址,为空时使用内嵌服务 ”IP:PORT“ 作为注册地址。从而更灵活的支持容器类型执行器动态IP和动态映射端口问题。
    xxl.job.executor.address=
    ### 执行器IP [选填]:默认为空表示自动获取IP,多网卡时可手动设置指定IP,该IP不会绑定Host仅作为通讯实用;地址信息用于 "执行器注册" 和 "调度中心请求并触发任务";
    xxl.job.executor.ip=
    ### 执行器端口号 [选填]:小于等于0则自动获取;默认端口为9999,单机部署多个执行器时,注意要配置不同执行器端口;
    xxl.job.executor.port=9999
    ### 执行器运行日志文件存储磁盘路径 [选填] :需要对该路径拥有读写权限;为空则使用默认路径;
    xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler
    ### 执行器日志文件保存天数 [选填] : 过期日志自动清理, 限制值大于等于3时生效; 否则, 如-1, 关闭自动清理功能;
    xxl.job.executor.logretentiondays=30

8.添加配置文件XxlJobConfig.java

9.添加测试任务AdminTask.java 这里只写了一个Bean模式的实例,需要使用其他可以查阅文档

10.如果以上步骤配置正确,在执行器管理中可以看到自己应用内配置的xxl:job:appname的值的执行器。打开任务管理,点击新增,按照图中设置好任务模板,有一点需要注意的是,JobHandler和测试任务注解的值时对应的。通过查看日志即可知道是否执行成功。

 


后续有时间还会更新记录

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值