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。意思就是我们的代码不在项目里面进行维护,是将代码写在调度中心的平台上,实时的更新。剩下的模式大家可以看文档。