Java Spring Boot计划作业

时间:2019-11-14 11:55:27

标签: java scheduler

我有一个包含一些工作的应用程序,我想将这些工作保留在数据库中,以便进行更好的维护。

应用的结构如下:

数据库:

CREATE TABLE jobs_config (
    id int8 NOT NULL GENERATED BY DEFAULT AS IDENTITY,
    job_name varchar NOT NULL,
    scheduled_value varchar NOT NULL,
    CONSTRAINT jobs_config_pk PRIMARY KEY (id),
    CONSTRAINT jobs_config_name_un UNIQUE (job_name)
);
insert into jobs_config(job_name, scheduled_value) values('DeleteExpiredTokenJob', '0 0 10 * * MON');

JAVA SPRING BOOT:

@Entity
@Table(name = "jobs_config")
public class JobsConfig {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "job_name")
    private String jobName;

    private String scheduledValue;

    public JobsConfig() {
    }

    public JobsConfig(String jobName, String scheduledValue) {
        this.jobName = jobName;
        this.scheduledValue = scheduledValue;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getJobName() {
        return jobName;
    }

    public void setJobName(String jobName) {
        this.jobName = jobName;
    }

    public String getScheduledValue() {
        return scheduledValue;
    }

    public void setScheduledValue(String scheduledValue) {
        this.scheduledValue = scheduledValue;
    }
}

@Service
public class JobSchedulerService implements SchedulingConfigurer {

    private static Logger logger = LoggerFactory.getLogger(JobSchedulerService.class);

    @Autowired
    JobsConfigRepository jobsConfigRepository;

    @Autowired
    DeleteExpiredTokenJob deleteExpiredTokenJob;

    @Autowired
    TestJob testJob;

    @Bean
    public TaskScheduler poolScheduler() {
        ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
        scheduler.setThreadNamePrefix("ThreadPoolTaskScheduler");
        scheduler.setPoolSize(1);
        scheduler.initialize();
        return scheduler;
    }

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        taskRegistrar.setTaskScheduler(poolScheduler());
        listJobList();
//        refreshJobList( poolScheduler());
    }

    public void listJobList() {
        List<JobsConfig>  jobsList = jobsConfigRepository.findAll();
        for (JobsConfig jobName : jobsList) {
            switch (jobName.getJobName()) {
                case "DeleteExpiredTokenJob":
                    scheduleJob(poolScheduler(), deleteExpiredTokenJob, jobName.getJobName());
                    break;
                case "TestJob":
                    scheduleJob(poolScheduler(), testJob, jobName.getJobName());
                    break;
                default:
                    logger.info(String.format("JOB NOT FOUND [%s]", jobName.getJobName()));
            }
        }
    }

    public void scheduleJob(TaskScheduler scheduler, JobInterface jobInterface, String jobName){
        scheduler.schedule(new Runnable(){
            @Override
            public void run() {
                jobInterface.jobCode();
            }
        }, new Trigger(){
            @Override
            public Date nextExecutionTime(TriggerContext triggerContext) {
                Optional <JobsConfig> job = jobsConfigRepository.findByJobName(jobName);
                String cronExp = job.get().getScheduledValue();
                return new CronTrigger(cronExp).nextExecutionTime(triggerContext);
            }
        });
    }
    //THIS IS WHAT I'VE TRIED !
    /*private void refreshJobList(TaskScheduler scheduler){
        scheduler.schedule(new Runnable(){
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+" The Task2 executed at "+ new Date());
                listJobList();
            }
        }, new Trigger(){
            @Override
            public Date nextExecutionTime(TriggerContext triggerContext) {
                String cronExp="0/10 * * * * ?";//Can be pulled from a db . This will run every minute
                return new CronTrigger(cronExp).nextExecutionTime(triggerContext);
            }
        });
    }*/

}

这个想法是,每当我在jobs_config表中添加一条记录时,我都需要将此反映在java中。

因此,如果我在表中添加新作业,则希望在不重新启动应用程序的情况下就可以使用(当然,存在DeleteExpiredTokenJob的Java代码)。

insert into jobs_config(job_name, scheduled_value) values('DeleteExpiredTokenJob', '0/5 * * * * ?');

基本上,我需要通过List<JobsConfig> jobsList = jobsConfigRepository.findAll();方法刷新列表listJobList()

我该如何实现?

1 个答案:

答案 0 :(得分:2)

我看到您已经尝试创建一个可以刷新作业的作业,但是没有用。

在将 new 作业添加到数据库中之后,我看不到简单的方法,假定它有一个新的@Autowired作业要注入,但是我想我知道如何跟踪数据库中的更新作业,即scheduled_value列。

  1. 在这里保留旧的JobsConfig值。

  2. 检查是否有任何更改。

    2.a。如果更改,请取消下一个作业执行,并计划更新的作业。

  3. 睡眠几分钟以进行下一次检查。 (就像您在注释代码中所做的一样)

    Map<String, JobsConfig> oldJobsConfigs = new HashMap<>();
    Map<String, ScheduledFuture<?>> activeJobs = new HashMap<>()

    public void listJobList() {
        List<JobsConfig> jobsList = jobsConfigRepository.findAll();
        for (JobsConfig jobName : jobsList) {
            // If this job was there before and has not changed, do nothing.
            if (oldJobsConfigs.containsKey(jobName.getJobName()) && oldJobsConfigs.get(jobName.getJobName()).getScheduledValue().equals(jobName.getScheduledValue())) 
                break;

            // Cancel previous execution, if any.
            if (activeJobs.containsKey(jobName.getJobName()) {
                ScheduledFuture<?> job = activeJobs.get(jobName.getJobName());
                job.cancel(false);
                try {
                    job.get(); // Warning! If the job is running, blocks current thread until the job finishes. If has an endless loop, it will block current thread forever.
                } catch (CancellationException e) {
                    // Do nothing, this is good, we did not spent time waiting for the job to finish.
                } catch (InterruptedException | ExecutionException e) {
                    // Log it?
                }
            }

            ScheduledFuture<?> newJob = null;
            switch (jobName.getJobName()) {
                case "DeleteExpiredTokenJob":
                    newJob = scheduleJob(poolScheduler(), deleteExpiredTokenJob, jobName.getJobName());
                    break;
                case "TestJob":
                    newJob = scheduleJob(poolScheduler(), testJob, jobName.getJobName());
                    break;
                default:
                    logger.info(String.format("JOB NOT FOUND [%s]", jobName.getJobName()));
            }

            if (newJob != null)
                activeJobs.put(jobName.getJobName(), newJob);
        }
    }

并更改scheduleJob签名,以便利用scheduler返回的信息。

    public ScheduledFuture<?> scheduleJob(TaskScheduler scheduler, JobInterface jobInterface, String jobName) {
        return scheduler.schedule(new Runnable() {
// ... unchanged

希望有帮助。 :D

UPD: 如果有一天您的工作数量超过十,那么您可以稍微调整JobInterface,以便返回其工作名称:

    public interface JobInterface {
        // ... old methods
        String getJobName(); // Consider switching to enums? 
    }

然后让Spring将JobInterface的所有实现自动连接到JobSchedulerService

@Service
public class JobSchedulerService implements SchedulingConfigurer {
    @Autowired
    JobsConfigRepository jobsConfigRepository;

    @Autowired
    List<JobInterface> allJobs;

然后,您不必编写很多条目的开关

    ScheduledFuture<?> newJob = null;
    for(JobInterface job : allJobs)
        if (job.getJobName().equals(jobName.getJobName()))
            newJob = scheduleJob(/*arguments*/);

    if (newJob == null)
        logger.warn(/*swear loudly :)*/);

也就是说,仅当您要删除此switch时。如果您打算减少工作量,那么也可以保留原样。

相关问题