1.java实现定时任务的四种方案

1.1:Thread

/**
 * 定时任务----Thread
 * Created by lizhen on 2018/4/9 0009.
 */
public class TimeTask001 {
    public static int i = 0;
    public static void main(String[] args) {

        new Thread(new Runnable() {

            @Override
            public void run() {
                while(true){
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("我是定时任务:"+ ++i);
                }

            }
        }).start();
    }
}

1.2:TimerTask

 

import java.util.Timer;
import java.util.TimerTask;

/**
 * 定时任务====TimeTask
 * Created by lizhen on 2018/4/9 0009.
 */
public class TimeTask002 {
    public static int i= 0;
    public static void main(String[] args) {
        //执行任务代码
        TimerTask timerTask = new TimerTask() {
            @Override
            public void run() {
                System.out.println("我是定时任务:"+ ++i);
            }
        };
        Timer timer = new Timer();
        //天数
        long day = 0;
        //秒数
        long mili = 1000;
        timer.schedule(timerTask,day,mili);
    }
}

1.3:newSingleThreadScheduledExecuto(定时的单线程池)

 

/**
 * 定时任务====创建可定时的线程池
 * Created by lizhen on 2018/4/9 0009.
 */
public class TimeTask003 {

    public static void main(String[] args) {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("马上就要辞职,需要抓紧准备。。。。。");
            }
        };
        ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
        // 第二个参数为首次执行的延时时间,第三个参数为定时执行的间隔时间
        scheduledExecutorService.scheduleAtFixedRate(runnable, 1, 1, TimeUnit.SECONDS);
    }
}

1.4:引入Quartz框架执行定时任务

1.4.1:引入maven依赖

 

<!-- quartz 定时任务调度框架 -->
<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz</artifactId>
    <version>2.2.1</version>
</dependency>
<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz-jobs</artifactId>
    <version>2.2.1</version>
</dependency>

1.4.2:创建Quartz的启动类

 

/**
 * 定时任务====quartz启动类
 * Created by lizhen on 2018/4/9 0009.
 */
public class StartJob {
    public static void main(String[] args) throws SchedulerException {
        //1.创建Scheduler的工厂
        SchedulerFactory sf = new StdSchedulerFactory();

        //2.从工厂中获取调度器实例
        Scheduler scheduler = sf.getScheduler();

        //3.创建JobDetail
        JobDetail jb = JobBuilder.newJob(MyJob.class)
                .withDescription("this is a ram job") //job的描述
                .withIdentity("ramJob", "ramGroup") //job 的name和group
                .build();

        //任务运行的时间,SimpleSchedle类型触发器有效
        long time = System.currentTimeMillis() + 3 * 1000L; //3秒后启动任务

        Date statTime = new Date(time);

        //4.创建Trigger
        //使用SimpleScheduleBuilder或者CronScheduleBuilder
        Trigger t = TriggerBuilder.newTrigger()
                .withDescription("")
                .withIdentity("ramTrigger", "ramTriggerGroup")
                //.withSchedule(SimpleScheduleBuilder.simpleSchedule())
                .startAt(statTime)  //默认当前时间启动,下面的用到了Quartz表达式:http://cron.qqe2.com/
                .withSchedule(CronScheduleBuilder.cronSchedule("0/2 * * * * ?")) //两秒执行一次
                .build();

        //5.注册任务和定时器
        scheduler.scheduleJob(jb, t);

        //6.启动 调度器
        scheduler.start();
    }
}

1.4.3:创建执行任务的具体类

 

/**
 * 定时任务====quartz框架实现
 * Created by lizhen on 2018/4/9 0009.
 */
public class MyJob implements Job {
    public void execute(JobExecutionContext context) throws JobExecutionException {
        System.out.println("quartz MyJob date:" + new Date().getTime());
    }
}

2.分布式Job

 

2.1在说分布式job之前,我们先考虑一下,传统的定时任务调度的缺点?

1.没有补偿机制,一旦出现异常,那么就不会执行,需要等到第二次的任务才能执行。

2.不支持集群,不支持路由策略(就是不能轮训)

3.没有管理平台,无法做统计

4.没有报警邮箱,状态监控(比如有了异常,假如我们重复执行,仍然失败怎么办?)

因为传统的定时任务这么多缺点,所以我们才会使用分布式任务调度平台

2.2那么集群情况下,分布式job怎么解决幂等性?

1.使用分布式锁,(ZK,redis)保证只有一台服务器在执行定时job

2.使用配置文件,配置文件上加上开关,flag=true的情况下才执行,false不执行。打包的时候,打不一样的配置文件的包。

3.使用数据库的唯一标识,谁插入进去,谁就执行,但是效率低,一般不会采用。

3.分布式任务调度平台之XXL—Job

XXL-RPC是一个分布式服务通讯框架,提供稳定高性能的RPC远程服务调用功能

官方文档地址:xxl的官方文档地址

3.1XXL设计思想

我们先将请求(任务JobHandler)注册到调度中心平台,而平台自身并不承担业务逻辑,“调度中心”负责发起调度请求。
将任务抽象成分散的JobHandler,交由“执行器”统一管理,“执行器”负责接收调度请求并执行对应的JobHandler中业务逻辑。因此,“调度”和“任务”两部分可以相互解耦,提高系统整体稳定性和扩展性;

3.2系统组成

任务:JobHandler,比如我要在handler里面记录日志,我要做的事情就是在handler里面。

执行器jetty:负责接收调度请求的服务器,接收之后调用JobHandler去执行任务逻辑。

调度中心:将执行器和任务注册至调度中心,由调度中心根据配置发出调度请求。先配置执行器,然后创建任务,因为任务是由执行器统一管理。

上面是我自己的理解,我们再看下一官方解释。

调度模块(调度中心): 负责管理调度信息,按照调度配置发出调度请求,自身不承担业务代码。调度系统与任务解耦,提高了系统可用性和稳定性,同时调度系统性能不再受限于任务模块; 支持可视化、简单且动态的管理调度信息,包括任务新建,更新,删除,GLUE开发和任务报警等,所有上述操作都会实时生效,同时支持监控调度结果以及执行日志,支持执行器Failover。

执行模块(执行器): 负责接收调度请求并执行任务逻辑。任务模块专注于任务的执行等操作,开发和维护更加简单和高效; 接收“调度中心”的执行请求、终止请求和日志请求等。

3.1配置部署调度中心

自己修改后的源码: https://github.com/lizhen376751/xxl-job-master.git

XXL-job的GitHub的源码:   https://github.com/xuxueli/xxl-job

Quartz的cron的表达式地址 : http://cron.qqe2.com/

下载我的源码之后就是这种结构

/xxl-job/xxl-job-admin/src/main/resources/xxl-job-admin.properties   调度中心配置的配置,JDBC链接,报警邮箱,登录账号都在这个配置文件里面配置

需要注意jdbc的配置:xxl.job.db.url=jdbc:mysql://localhost:3306/xxl-job?useUnicode=true&characterEncoding=UTF-8

创建xxl-job的数据库,同时将xxl-job-master\xxl-job-master\doc\db\tables_xxl_job.sql 里面的sql语句黏贴复制,并执行所有的sql语句。其它的配置文件不需要修改,tomcat启动即可。

调度中心访问地址:http://localhost:8080/xxl-job-admin (该地址执行器将会使用到,作为回调地址,在执行器项目里面我们也会将这个注册地址进行配置,后面的项目名要么都加要么都去掉,我是没有加的),登录后运行界面如下图所示

3.2配置部署执行器

我是用springboot进行配置的,我的源码中已经配置好了,下面的教程是在另一个项目中去搭建的执行器

1.引入maven文件

 

<!--XXL-job-->
<dependency>
    <groupId>org.eclipse.jetty</groupId>
    <artifactId>jetty-server</artifactId>
</dependency>
<dependency>
    <groupId>org.eclipse.jetty</groupId>
    <artifactId>jetty-util</artifactId>
</dependency>
<dependency>
    <groupId>org.eclipse.jetty</groupId>
    <artifactId>jetty-http</artifactId>
</dependency>
<dependency>
    <groupId>org.eclipse.jetty</groupId>
    <artifactId>jetty-io</artifactId>
</dependency>

<dependency>
    <groupId>com.xuxueli</groupId>
    <artifactId>xxl-job-core</artifactId>
    <version>1.8.2</version>
</dependency>
<!--XXL-job-->

2.从调度中心我的源码中,在spring-boot的demo中将logback.xml和XxlJobConfig.java复制到新建的项目中。

3.在application.properties配置执行器的相关配置

 

#XXL相关配置
logging.config=classpath:logback.xml

### xxl-job admin address list:调度中心部署跟地址:如调度中心集群部署存在多个地址则用逗号分隔。执行器将会使用该地址进行"执行器心跳注册"和"任务结果回调"。
xxl.job.admin.addresses=http://127.0.0.1:8081

### xxl-job executor address:执行器"AppName"和地址信息配置:AppName执行器心跳注册分组依据;
#地址信息用于"调度中心请求并触发任务"和"执行器注册"。
#执行器默认端口为9999
xxl.job.executor.appname=myjobtest
xxl.job.executor.ip=192.168.1.153
xxl.job.executor.port=9999

### xxl-job log path 执行器运行日志文件存储的磁盘位置,需要对该路径拥有读写权限
xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler/

4.创建任务JobHandler

 

import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.handler.IJobHandler;
import com.xxl.job.core.handler.annotation.JobHander;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;


/**
 * xxl-job 自己写的定时任务
 * Created by lizhen on 2018/4/9 0009.
 * <p>
 * 任务Handler的一个Demo(Bean模式)
 * <p>
 * 开发步骤:
 * 1、继承 “IJobHandler” ;
 * 2、装配到Spring,例如加 “@Service” 注解;
 * 3、加 “@JobHander” 注解,注解value值为新增任务生成的JobKey的值;多个JobKey用逗号分割;
 * 4、执行日志:需要通过 "XxlJobLogger.log" 打印执行日志;
 */
@Service
@JobHander("myjobhandler")
public class MyJobHandler extends IJobHandler {

    @Value("${xxl.job.executor.port}")
    private String port;

    @Override
    public ReturnT<String> execute(String... strings) throws Exception {
        System.out.println("定时任务调度成功===="+strings[0]+"===="+strings[1]+"=========="+strings[2]+"======+port:" + port);
        ReturnT.SUCCESS.setContent("success good good");
        ReturnT.SUCCESS.setMsg("hello word!");
        return ReturnT.SUCCESS;
    }
}

5.启动项目即可,新项目的目录结构如图所示:

3.3将执行器和任务注册至调度中心

配置执行器

新建任务

cron表达式的配置  http://cron.qqe2.com/

查看调度日志

 BEAN模式:任务逻辑以JobHandler的形式存在于“执行器”就是文章所演示的这种形式。

GLUE模式(Java):任务以源码方式维护在调度中心,支持通过Web IDE在线更新,实时编译和生效,因此不需要指定JobHandler。意思就是我们的代码不在项目里面进行维护,是将代码写在调度中心的平台上,实时的更新。剩下的模式大家可以看文档。

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐