xml<!--quartz依赖-->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.2.3</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz-jobs</artifactId>
<version>2.2.3</version>
</dependency>
sqlCREATE TABLE `schedule_job` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
`name` varchar(100) NOT NULL COMMENT '任务名称',
`clazz` varchar(100) NOT NULL COMMENT '任务执行的类',
`cron` varchar(50) NOT NULL COMMENT 'cron表达式',
`status` int(11) NOT NULL DEFAULT '2' COMMENT '1 开启 2 关闭 ',
`job_group_name` varchar(50) NOT NULL DEFAULT 'DEFAULT_JOB_GROUP',
`trigger_group_name` varchar(50) NOT NULL DEFAULT 'DEFAULT_TRIGGER_GROUP',
`des` varchar(200) DEFAULT NULL COMMENT '描述',
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`update_time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
sqlCREATE TABLE `schedule_log` (
`id` varchar(32) NOT NULL COMMENT '主键',
`job_id` varchar(100) NOT NULL COMMENT '定时任务的id',
`job_name` varchar(100) DEFAULT NULL,
`job_clazz` varchar(100) DEFAULT NULL COMMENT '任务执行的类',
`status` int(11) NOT NULL DEFAULT '1' COMMENT '1 成功 2 异常 ',
`log_info` text COMMENT '描述',
`create_time` bigint(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
java/**
* @calssName JobSchedule
* @Description 任务调度的工具类
* @Author xxx
* @DATE 2020/5/28 11:20
*/
public class JobSchedule {
private static final Logger LOGGER = LoggerFactory.getLogger(JobSchedule.class);
private static SchedulerFactory gSchedulerFactory = new StdSchedulerFactory();
private static String JOB_GROUP_NAME = "DEFAULT_JOB_GROUP";
private static String TRIGGER_GROUP_NAME = "DEFAULT_TRIGGER_GROUP";
// 默认将 MyJobListener 绑定到 JOB_GROUP_NAME,在MyJobListener通过监听的方式记录 记录定时任务的执行记录
static {
try {
Scheduler sched = gSchedulerFactory.getScheduler();
sched.getListenerManager().addJobListener(new MyJobListener(),jobGroupEquals(JOB_GROUP_NAME));
}catch (Exception e){
LOGGER.error(">>>>>>>>>>>>>>>>>>>>>> 定时任务addJobListener失败 >>>>>>>>>>>>>>>>>>>>>>");
}
}
private JobSchedule() {
}
/**
* @Description: 添加一个定时任务,使用默认的任务组名,触发器名,触发器组名
* @param jobName 任务名
* @param cls 任务
* @param cron 时间设置,参考quartz说明文档
* @throws SchedulerException
*
*/
public static void addJob(String jobName, Class cls, String cron) throws SchedulerException {
Scheduler sched = gSchedulerFactory.getScheduler();
// 用于描叙Job实现类及其他的一些静态信息,构建一个作业实例
JobDetail jobDetail = JobBuilder.newJob(cls).withIdentity(jobName, JOB_GROUP_NAME).build();
// 构建一个触发器,规定触发的规则
Trigger trigger = TriggerBuilder.newTrigger()// 创建一个新的TriggerBuilder来规范一个触发器
.withIdentity(jobName, TRIGGER_GROUP_NAME)// 给触发器起一个名字和组名
.startNow()// 立即执行
.withSchedule(CronScheduleBuilder.cronSchedule(cron)) // 触发器的执行时间
.build();// 产生触发器
sched.scheduleJob(jobDetail, trigger);
LOGGER.info("添加任务:{},{},{}",jobName,cls,cron);
// 启动
if (!sched.isShutdown()) {
sched.start();
}
}
/**
* @Description: 添加一个定时任务
*
* @param jobName 任务名
* @param jobGroupName 任务组名
* @param triggerName 触发器名
* @param triggerGroupName 触发器组名
* @param cls 任务
* @param cron 时间设置,参考quartz说明文档
*/
public static void addJob(String jobName, String jobGroupName, String triggerName, String triggerGroupName, Class cls, String cron) throws SchedulerException {
Scheduler sched = gSchedulerFactory.getScheduler();
// 用于描叙Job实现类及其他的一些静态信息,构建一个作业实例
JobDetail jobDetail = JobBuilder.newJob(cls).withIdentity(jobName, jobGroupName).build();
// 构建一个触发器,规定触发的规则
Trigger trigger = TriggerBuilder.newTrigger()// 创建一个新的TriggerBuilder来规范一个触发器
.withIdentity(jobName, triggerGroupName)// 给触发器起一个名字和组名
.startNow()// 立即执行
.withSchedule(CronScheduleBuilder.cronSchedule(cron)) // 触发器的执行时间
.build();// 产生触发器
sched.scheduleJob(jobDetail, trigger);
LOGGER.info("添加任务:{},{},{},{},{},{}",jobName,jobGroupName,triggerName,triggerGroupName,cls,cron);
// 启动
if (!sched.isShutdown()) {
sched.start();
}
}
/**
* @Description: 修改一个任务的触发时间(使用默认的任务组名,触发器名,触发器组名)
*
* @param jobName
* @param cron
* @throws SchedulerException
*/
public static void modifyJobTime(String jobName, String cron) throws SchedulerException {
Scheduler sched = gSchedulerFactory.getScheduler();
TriggerKey triggerKey = new TriggerKey(jobName, TRIGGER_GROUP_NAME);
CronTrigger trigger = (CronTrigger) sched.getTrigger(triggerKey);
if (trigger == null) {
return;
}
String oldTime = trigger.getCronExpression();
if (!oldTime.equalsIgnoreCase(cron)) {
JobDetail jobDetail = sched.getJobDetail(new JobKey(jobName, JOB_GROUP_NAME));
Class objJobClass = jobDetail.getJobClass();
removeJob(jobName);
addJob(jobName, objJobClass, cron);
LOGGER.info("修改任务:{},{}",jobName,cron);
}
}
/**
* @Description: 修改或添加一个任务,jobName 存在时修改任务,不存在时则新增任务
*/
public static void modifyOrAddJobTime(String jobName, String cron, Class cls) throws SchedulerException {
Scheduler sched = gSchedulerFactory.getScheduler();
TriggerKey triggerKey = new TriggerKey(jobName, TRIGGER_GROUP_NAME);
CronTrigger trigger = (CronTrigger) sched.getTrigger(triggerKey);
if (trigger == null) { // 此任务不存在
addJob(jobName, cls, cron);
LOGGER.info("修改任务:{},{}",jobName,cron);
}else{
String oldTime = trigger.getCronExpression();
if (!oldTime.equalsIgnoreCase(cron)) {
JobDetail jobDetail = sched.getJobDetail(new JobKey(jobName, JOB_GROUP_NAME));
Class objJobClass = jobDetail.getJobClass();
removeJob(jobName);
addJob(jobName, objJobClass, cron);
LOGGER.info("修改任务:{},{}",jobName,cron);
}
}
}
/**
* @Description: 移除一个任务(使用默认的任务组名,触发器名,触发器组名)
* @param jobName
* @throws SchedulerException
*/
public static void removeJob(String jobName) throws SchedulerException {
Scheduler sched = gSchedulerFactory.getScheduler();
JobKey jobKey = new JobKey(jobName, TRIGGER_GROUP_NAME);
// 停止触发器
sched.pauseJob(jobKey);
sched.unscheduleJob(new TriggerKey(jobName, TRIGGER_GROUP_NAME));// 移除触发器
sched.deleteJob(jobKey);// 删除任务
LOGGER.info("移除任务:{}",jobName);
}
/**
* 移除任务
*
* @param jobName
* @param jobGroupName
* @param triggerName
* @param triggerGroupName
* @throws SchedulerException
*/
public static void removeJob(String jobName, String jobGroupName, String triggerName, String triggerGroupName) throws SchedulerException {
Scheduler sched = gSchedulerFactory.getScheduler();
JobKey jobKey = new JobKey(jobName, jobGroupName);
// 停止触发器
sched.pauseJob(jobKey);
sched.unscheduleJob(new TriggerKey(jobName, triggerGroupName));// 移除触发器
sched.deleteJob(jobKey);// 删除任务
LOGGER.info("移除任务:{},{},{},{},{},{}",jobName,jobGroupName,triggerName,triggerGroupName);
}
/**
* 启动所有任务
*
* @throws SchedulerException
*/
public static void startJobs() throws SchedulerException {
Scheduler sched = gSchedulerFactory.getScheduler();
sched.start();
LOGGER.info("启动所有任务");
}
/**
* 关闭所有定时任务
* @throws SchedulerException
*/
public static void shutdownJobs() throws SchedulerException {
Scheduler sched = gSchedulerFactory.getScheduler();
if (!sched.isShutdown()) {
sched.shutdown();
LOGGER.info("关闭所有任务");
}
}
}
java/**
* @calssName MyJobListener
* @Description 定时任务监听器:通过监听的方式记录 记录定时任务的执行记录
* @Author xxx
* @DATE 2020/5/29 14:18
*/
@Component
public class MyJobListener implements JobListener{
private JobService jobService;
// 用于保存 logId
ThreadLocal<String> threadLocal = new ThreadLocal<>();
@Override
public String getName() {
return "myJobListener";
}
// 执行开始新增一条执行日志,状态为执行中
@Override
public void jobToBeExecuted(JobExecutionContext jobExecutionContext) {
if(jobService == null){
jobService = SpringUtils.getBean(JobService.class);
}
JobDetail jobDetail = jobExecutionContext.getJobDetail();
String name = jobDetail.getKey().getName();
String logId = jobService.saveScheduleLog(name, JobLogStatusEnum.RUNNING.getCode(), "");
threadLocal.set(logId); // 把执行记录放到 threadLocal,提供给执行结束后取此结果
}
// 任务拒绝执行时,修改日志状态为:执行失败
@Override
public void jobExecutionVetoed(JobExecutionContext jobExecutionContext) {
String logId = threadLocal.get(); // 执行记录logId
jobService.updateScheduleLog(logId, JobLogStatusEnum.ERROR.getCode(), "执行失败");
}
// 任务执行时结果,判断是否有异常,修改对应状态
@Override
public void jobWasExecuted(JobExecutionContext jobExecutionContext, JobExecutionException e) {
String logId = threadLocal.get(); // 执行记录logId
if(e == null){ // 没有异常修改记录为成功
jobService.updateScheduleLog(logId, JobLogStatusEnum.SUCCESS.getCode(), "执行成功");
}else{ // 存在异常修改记录失败,并且把异常信息保存到数据库中
jobService.updateScheduleLog(logId, JobLogStatusEnum.ERROR.getCode(), e.toString());
}
}
}
java@Configuration
public class ScheduleJobInitConfig implements ServletContextAware {
private static final Logger logger = LoggerFactory.getLogger(ScheduleJobInitConfig.class);
@Autowired
private JobService jobService;
@Override
public void setServletContext(ServletContext servletContext) {
jobService.initScheduleJob();
logger.info("初始化定时任务成功 ...");
}
}
java/**
* @calssName JobA
* @Description 每写一个定时任务需要在数据库中schedule_job中配置一条数据,game-admin项目初始化时会加载数据库中的已启动的定时任务
* @Author xxx
* @DATE 2020/5/28 11:14
*/
@Component
public class JobA implements Job {
private static final Logger logger = LoggerFactory.getLogger(JobA.class);
private JobService jobService;
@Override
public void execute(JobExecutionContext context) {
System.out.println("=================>JobA ");
// 这里写需要执行的业务逻辑
}
}
java/**
* @calssName ScheduleController
* @Description TODO
* @Author xxx
* @DATE 2020/5/28 12:26
*/
@RestController
@RequestMapping("/v1/schedule/job")
public class JobController {
@Autowired
private JobService jobService;
/**
* 查看所有任务列表
*/
@GetMapping("/list")
public R getJobList(){
try{
Map<String, Object> result = jobService.getAllJob();
return R.data(result);
}catch (Exception e){
return R.error(e);
}
}
/**
* 查看某个任务的执行详情
*/
@GetMapping("/schedule/detail")
public R getJobScheduleDetail(@RequestParam("jobId") int jobId){
try{
Map<String, Object> result = jobService.getJobScheduleDetail(jobId);
return R.data(result);
}catch (Exception e){
return R.error(e);
}
}
/**
* 查看某个任务的详情
*/
@GetMapping("/detail")
public R getJobDetail(@RequestParam("jobId") int jobId){
try{
Map<String, Object> result = jobService.getJobDetail(jobId);
return R.data(result);
}catch (Exception e){
return R.error(e);
}
}
/**
* 修改任务
*/
@PostMapping("/update")
public R updateJob(ScheduleJob job){
try{
Map<String, Object> result = jobService.updateJob(job);
return R.data(result);
}catch (Exception e){
return R.error(e);
}
}
}
java/**
* @calssName JobService
* @Description
* @Author xxx
* @DATE 2020/5/28 15:59
*/
@Service
public class JobService {
private static final Logger logger = LoggerFactory.getLogger(JobService.class);
@Autowired
private ScheduleJobRepoImpl scheduleJobRepo;
@Autowired
private ScheduleLogRepoImpl scheduleLogRepo;
/**
* 查看所有任务列表
*/
public Map<String,Object> getAllJob(){
List<ScheduleJob> list = scheduleJobRepo.getList();
Map<String,Object> result = new HashMap<>();
result.put("list", list);
return result;
}
/**
* 查看某个任务的执行详情
*/
public Map<String,Object> getJobScheduleDetail(int jobId){
List<ScheduleLog> list = scheduleLogRepo.getListByJobId(jobId);
Map<String,Object> result = new HashMap<>();
result.put("list", list);
return result;
}
/**
* 查看某个任务的详情
*/
public Map<String,Object> getJobDetail(int jobId){
ScheduleJob scheduleJob = scheduleJobRepo.getScheduleJobById(jobId);
Map<String,Object> result = new HashMap<>();
result.put("scheduleJob", scheduleJob);
return result;
}
/**
* 修改任务
*/
public Map<String,Object> updateJob(ScheduleJob scheduleJob){
Map<String,Object> result = new HashMap<>();
Class<?> clszz = null;
try {
clszz = Class.forName(scheduleJob.getClazz());
}catch (ClassNotFoundException e){
logger.error("修改定时任务异常,无法找到指定的任务类");
throw new AppException(ErrorCode.SYS_PARAMS_ERROR.code(), "无法找到指定的任务类");
}
try {
Integer status = scheduleJob.getStatus();
if(status == 1){ // 修改或者添加任务
JobSchedule.modifyOrAddJobTime(scheduleJob.getName(), scheduleJob.getCron(),clszz);
}else if(status == 2){ // 停止任务
JobSchedule.removeJob(scheduleJob.getName());
}else{
return result;
}
}catch (SchedulerException e){
logger.error("修改定时任务异常:{}", e);
throw new AppException(ErrorCode.SYS_ERROR);
}
scheduleJobRepo.updateScheduleJob(scheduleJob);
return result;
}
/**
* 启动数据库中配置的定时任务
*/
public void initScheduleJob(){
List<ScheduleJob> list = scheduleJobRepo.getEnableList();
for (ScheduleJob scheduleJob: list){
Class<?> clszz = null;
try {
clszz = Class.forName(scheduleJob.getClazz());
JobSchedule.addJob(scheduleJob.getName(), clszz, scheduleJob.getCron());
} catch (SchedulerException e){
logger.error("初始化启动定时任务异常:{}", e);
continue;
} catch (ClassNotFoundException e){
logger.error("初始化启动定时任务异常,无法找到指定的任务类");
continue;
}
}
}
/**
* 新增一条执行日志
*/
public String saveScheduleLog(String name, int status, String logInfo){
ScheduleJob scheduleJob = scheduleJobRepo.getScheduleJobByName(name);
ScheduleLog scheduleLog = new ScheduleLog(scheduleJob,status,logInfo);
scheduleLogRepo.saveScheduleLog(scheduleLog);
return scheduleLog.getId();
}
/**
* 更新一条执行日志
*/
public void updateScheduleLog(String id, int status, String logInfo){
scheduleLogRepo.updateScheduleLog(id,status,logInfo);
}
}
本文作者:jiangshaoneng
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!