Opening notes
- If you've got inspiration and thinking here, I hope you'll like it! For different opinions on the content, please write and exchange.
- Technology stack > > java
- Email > > 15673219519@163.com
describe
- The system needs to use timed tasks to complete some data processing work.
- It is hoped that a page can control the execution plan of the task and record the results of each execution. If there is any exception, record it to the database.
- quartz official website gives 11 tables to work together. In fact, it doesn't need so many functions (maybe your project needs them).
- I think two kinds of tables can do the daily work_ Job, schedule_ Log (task execution record table).
My thoughts
- Two types of tables are created to store scheduled task information and execution record information. – step 1
- It is necessary to record the status results of each execution. During execution, execution failed, execution succeeded, etc. it is impossible to write in each Job class. Originally, it was intended to use aop, but it has not been effective. Later, it was found that it could be implemented through a JobListener listener. It should be noted here that to add a record at the beginning of execution and update the record at the end of execution or exception, in order to ensure that the log ID of the record is obtained, put the log ID in ThreadLocal at the beginning of execution and the log ID obtained from ThreadLocal at the end of execution. – step 3
- The JobListener listener needs to listen to those tasks, which need to be specified when the JobSchedule is initialized. In the static {} static code block in step 2
- In order to ensure that the project in the startup state in the database starts normally when the project starts, it is necessary to start the tasks in the database when the project is initialized. – step 4
- Through page control, I need to provide some interfaces, such as adding a scheduled task, modifying the execution plan / starting or closing, querying the execution record of a task, etc.
- The operation state of the timed task needs to be updated to the database synchronously, so I invoke the JobSchedule tool class in service to control the task execution, and at the same time the information status of the new data.
-
Load dependency
- If it is configured, I use the default. (no writing is the default)
<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>
Step 1: create a database
- schedule_job: used to record the information of each scheduled task
CREATE TABLE `schedule_job` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'Primary key',
`name` varchar(100) NOT NULL COMMENT 'Task name',
`clazz` varchar(100) NOT NULL COMMENT 'Task execution class',
`cron` varchar(50) NOT NULL COMMENT 'cron expression',
`status` int(11) NOT NULL DEFAULT '2' COMMENT '1 On 2 off ',
`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 'describe',
`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;
- schedule_log: used to record the execution details of scheduled tasks
CREATE TABLE `schedule_log` (
`id` varchar(32) NOT NULL COMMENT 'Primary key',
`job_id` varchar(100) NOT NULL COMMENT 'Scheduled task id',
`job_name` varchar(100) DEFAULT NULL,
`job_clazz` varchar(100) DEFAULT NULL COMMENT 'Task execution class',
`status` int(11) NOT NULL DEFAULT '1' COMMENT '1 Success 2 exception ',
`log_info` text COMMENT 'describe',
`create_time` bigint(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Step 2: create a tool class for scheduled tasks
- The JobSchedule tool class provides the addition, modification and deletion of tasks.
- To provide control pages for timed tasks, it is to invoke the tool class in Controller corresponding service to realize the control of timed tasks. (there will be code of Controller - service)
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";
static {
try {
Scheduler sched = gSchedulerFactory.getScheduler();
sched.getListenerManager().addJobListener(new MyJobListener(),jobGroupEquals(JOB_GROUP_NAME));
}catch (Exception e){
LOGGER.error(">>>>>>>>>>>>>>>>>>>>>> Scheduled tasks addJobListener fail >>>>>>>>>>>>>>>>>>>>>>");
}
}
private JobSchedule() {
}
public static void addJob(String jobName, Class cls, String cron) throws SchedulerException {
Scheduler sched = gSchedulerFactory.getScheduler();
JobDetail jobDetail = JobBuilder.newJob(cls).withIdentity(jobName, JOB_GROUP_NAME).build();
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity(jobName, TRIGGER_GROUP_NAME)
.startNow()
.withSchedule(CronScheduleBuilder.cronSchedule(cron))
.build();
sched.scheduleJob(jobDetail, trigger);
LOGGER.info("Add task:{},{},{}",jobName,cls,cron);
if (!sched.isShutdown()) {
sched.start();
}
}
public static void addJob(String jobName, String jobGroupName, String triggerName, String triggerGroupName, Class cls, String cron) throws SchedulerException {
Scheduler sched = gSchedulerFactory.getScheduler();
JobDetail jobDetail = JobBuilder.newJob(cls).withIdentity(jobName, jobGroupName).build();
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity(jobName, triggerGroupName)
.startNow()
.withSchedule(CronScheduleBuilder.cronSchedule(cron))
.build();
sched.scheduleJob(jobDetail, trigger);
LOGGER.info("Add task:{},{},{},{},{},{}",jobName,jobGroupName,triggerName,triggerGroupName,cls,cron);
if (!sched.isShutdown()) {
sched.start();
}
}
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("Modify task:{},{}",jobName,cron);
}
}
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("Modify task:{},{}",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("Modify task:{},{}",jobName,cron);
}
}
}
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("Remove task:{}",jobName);
}
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("Remove task:{},{},{},{},{},{}",jobName,jobGroupName,triggerName,triggerGroupName);
}
public static void startJobs() throws SchedulerException {
Scheduler sched = gSchedulerFactory.getScheduler();
sched.start();
LOGGER.info("Start all tasks");
}
public static void shutdownJobs() throws SchedulerException {
Scheduler sched = gSchedulerFactory.getScheduler();
if (!sched.isShutdown()) {
sched.shutdown();
LOGGER.info("Close all tasks");
}
}
}
Step 3: MyJobListener
- Scheduled task listener: record and record the execution records of scheduled tasks by listening.
- When creating a log, put the logId in ThreadLocal. In the execution result, get the logId from ThreadLocal to update the log status.
@Component
public class MyJobListener implements JobListener{
private JobService jobService;
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);
}
@Override
public void jobExecutionVetoed(JobExecutionContext jobExecutionContext) {
String logId = threadLocal.get();
jobService.updateScheduleLog(logId, JobLogStatusEnum.ERROR.getCode(), "Execution failed");
}
@Override
public void jobWasExecuted(JobExecutionContext jobExecutionContext, JobExecutionException e) {
String logId = threadLocal.get();
if(e == null){
jobService.updateScheduleLog(logId, JobLogStatusEnum.SUCCESS.getCode(), "Execution successful");
}else{
jobService.updateScheduleLog(logId, JobLogStatusEnum.ERROR.getCode(), e.toString());
}
}
}
Step 4: make sure the scheduled task is started when restarting the project
- You need to add the tasks started in the data to the database when the project is initialized
- This class will execute: xxx implements ServletContextAware after the project is started
@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("Initialization of scheduled task succeeded ...");
}
}
Step 5: create a scheduled task
@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 ");
}
}
The interface method of the control task. The query for adding and deleting tasks is in the following service class
- To complete the above, the timing task module is basically completed. The next step is to control the interface provided by the interface. So I use postman to call interface control directly.
- controller
@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);
}
}
}
@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("The specified task class cannot be found due to the exception of modifying the scheduled task");
throw new AppException(ErrorCode.SYS_PARAMS_ERROR.code(), "Unable to find the specified task class");
}
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("Exception in modifying scheduled task:{}", 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("Initialization start timing task exception:{}", e);
continue;
} catch (ClassNotFoundException e){
logger.error("Initialization start timer task exception, unable to find the specified task class");
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);
}
}
last
- If you've got inspiration and thinking here, I hope you'll like it! For different opinions on the content, please write and exchange.
- Technology stack > > java
- Email > > 15673219519@163.com