热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Spring-boot使用Quartz实现多线程调度任务

背景:项目的背景还原一下,项目的App需要去调用第三方的接口获取数据。我们要创建很多个任务(Task),每个Task都要去获取数据,但是每个Task的执行周期和频率不一样。所以使用多线程


背景:项目的背景还原一下,项目的App需要去调用第三方的接口获取数据。我们要创建很多个任务(Task),每个Task都要去获取数据,但是每个Task的执行周期和频率不一样。所以使用多线程来解决。为每一个Task启动一个线程。然后循环不停的去获取数据。刚开始的时候打算自己写Thread然后对Thread进行管理。后面发现有一个框架Quartz可以很Spring-boot进行集成。非常好用,但是使用的过程中还是遇到了很多的问题。自己网上查资料找结果才将问题最后都解决了。在这个工程中深感查询资料的不容易,很多人写的博客表述不清楚。所以自己写一个希望可以帮助到更多的人。

因为Quartz的用法网上还是很多的很容易找到。难的是如何和Spring-boot结合起来是比较麻烦的。所以我可能重点会放在这个上面。

具体实现步骤:

1、首先在项目的Gradle里面添加Quartz 依赖(Maven的话就自己找一个依赖的源)

compile('org.quartz-scheduler:quartz:2.3.0')  

一、实现观察者模式。实现一个Listener去观察Service的动向,减少耦合关系。

1、先让被观察者继承Java.util.Observable对象。表示可以拥有给观察者发送消息的能力

@Service
public class OutlierServiceImpl extends Observable {

@Override
public void addTask(OutlierDetection outlierDetection){

MessageObject messageObject =new MessageObject();
messageObject.setOperate(OutlierOperate.ADD);
messageObject.setOutlierDetection(outlierDetection);
setChanged();
notifyObservers(messageObject);
}
@Override
public void removeTask(String assetId,String outlierName){


setChanged();
notifyObservers(messageObject);
}

@Override
public void updateTask(OutlierDetection outlierDetection){
MessageObject messageObject=new MessageObject();
messageObject.setOperate(OutlierOperate.UPDATE);
messageObject.setOutlierDetection(outlierDetection);
setChanged();
notifyObservers(messageObject);
}

2、创建一个观察者实现Observer的接口。表示可以拥有观察的能力

@Component
public class OutlierServiceListener implements Observer{

//private Observable ob;
private String name;
private Scheduler scheduler;


@Autowired
OutlierServiceImpl outlierServiceImpl;

@Autowired
@Qualifier("schedulerFactoryBean")
private SchedulerFactoryBean schedulerFactoryBean;

@Autowired
JobFactory jobFactory;

@PostConstruct
public void registryOutlier(){
outlierServiceImpl.addObserver(this);
scheduler = schedulerFactoryBean.getScheduler();
}




@Override
public void update(Observable o, java.lang.Object arg) {

MessageObject messageObject =(MessageObject) arg;
if(messageObject.getOperate().equals(OutlierOperate.ADD)){
OutlierDetection outlier = messageObject.getOutlierDetection();
addJob(outlier);
}else if(messageObject.getOperate().equals(OutlierOperate.REMOVE)){
String outlierName = messageObject.getOutlierName();
String assetId = messageObject.getAssetId();
removeJob(assetId,outlierName);
}else if(messageObject.getOperate().equals(OutlierOperate.UPDATE)){
OutlierDetection outlier = messageObject.getOutlierDetection();
updateJob(outlier);
}

}

3.建立观察者与被观察者之间的联系

通过Spring的注解将outlierService注入到outlierListener里面。然后设置Outlier的观察者是自己。这样观察者就可以与被观察者联系起来了。(注解PostConstruct的意思是在初始化完整个类后会执行这个函数)

4.被观察者发出通知消息。观察者接收到通知消息然后执行相应的操作。

绿色方框里面是我自己定义的MessageObject。用于在观察者和被观察者之前传递消息。被观察者先配置好消息,然后调用SetChanged()表示被观察者已经有了变化。然后调用notifyObservers(messageObject)函数。通知所有的观察者。并将消息发送过去。

二、在Listener里面利用Quartz实现周期性执行任务

Quartz简单介绍一下:Quartz是一个完全由java编写的开源作业调度框架、可以按照计划创建简单或者复杂的几十,几百。甚至数十万的作业数。

重点:如何和Spring集成(Spring-boot这种没有xml配置的项目)如何集成。Quartz的使用方法网上介绍挺多的,也挺详细的。

1、 创建一个自己的job继承于Quartz的job,Overrideexecute函数。这个函数就是每一个job执行一次所要做的功能。把一个线程应该做的工作就写在里面就好了。

public class OutlierJob implements Job{

@Autowired
OutlierService outlierService;

OutlierReport lastReport;

@Override
public void execute(JobExecutionContext context){
JobDataMap dataMap = context.getJobDetail().getJobDataMap();
String name = getName(dataMap);
String assetId = getAssetId(dataMap);
System.out.println("Outlier task 【"+name+"】is running");
OutlierDetection outlierDetection=outlierService.getOutlierDetectionCfg(assetId,name);
if (lastReport==null){
lastReport = outlierService.findLastReportForTask(assetId,name);
}
lastReport = outlierService.runOutlierDetection(lastReport,outlierDetection);
}

public String getAssetId(JobDataMap dataMap){
return dataMap.getString("assetId");
}

public String getName(JobDataMap dataMap){
return dataMap.getString("name");
}
}

2、 创建Scheduler。给Scheduler添加job和job的trigger。然后将job加入到Scheduler里面。Job就会开始按照trigger设置的周期定时的执行了。

private void addJob(OutlierDetection outlierDetection){
String taskName = outlierDetection.getTaskName();
String assetId = outlierDetection.getAssetId();
int interval = new Long(outlierDetection.getInterval()).intValue();
JobDetail job = JobBuilder.newJob(OutlierJob.class)
.withIdentity(taskName,assetId).build();
job.getJobDataMap().put("name",taskName);
job.getJobDataMap().put("assetId",assetId);

Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity(taskName,assetId)
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(interval).repeatForever())
.build();
try {
scheduler.scheduleJob(job,trigger);
scheduler.start();
} catch (SchedulerException e) {
e.printStackTrace();
}
}
3、 停止一个Job。当job执行起来以后如何停止是应该需要考虑到的。不然就会一直执行了。先暂停Trigger。然后将Trigger移出Scheduler。最后deleteJob

 private void removeJob(String assetId,String jobName){
System.out.println("Job【"+jobName+"】exit");
try {
JobKey jobKey = JobKey.jobKey(jobName,assetId);
TriggerKey triggerKey = TriggerKey.triggerKey(jobName,assetId);
scheduler.pauseTrigger(triggerKey);
scheduler.unscheduleJob(triggerKey);
scheduler.deleteJob(jobKey);
outlierServiceImpl.deleteOutlierDetectionTask(assetId,jobName);
} catch (SchedulerException e) {
e.printStackTrace();
}
}

小结一下:在这里。我们已经完成了创建一个Listener去观察Service。当Service发出需要添加一个task的时候,Listener就能得到消息。然后在Quartz的Scheduler里面添加一个job。Job就会按照时间和周期定时的去执行我们之前写好在execute里面的代码。

问题:但是我们在job里面如何去调用由Spring管理的bean。这个问题就很麻烦了。因为我们每次添加一个task都是手动去new一个job。那么在new job的时候不是由Spring的容器在管理。所以在这种情况下,使用@autowired依赖注入Spring的bean类会出现注入不进来的情况。outlierService为空。这样我们就没有办法调用Spring的bean。经过各种查找,找到了解决这个问题的方法。

4、创建一个JobFactory类继承于AdaptableJobFactory。注入AutowireCapableBeansFactory.这样就完成Spring对Job的注入功能。

@Component
public class JobFactory extends AdaptableJobFactory{

@Autowired
private AutowireCapableBeanFactory capableBeanFactory;

@Override
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
//调用父类的方法
Object jobInstance = super.createJobInstance(bundle);
//进行注入
capableBeanFactory.autowireBean(jobInstance);
return jobInstance;
}

}
5、 添加一个QuartzConfig类。对Scheduer进行重新配置设置Scheduler的JobFactory使用我们自己创建的JobFactory
@Configurationpublic class QuartzConfig {    @Autowired    private JobFactory jobFactory;    @Bean(name = "schedulerFactoryBean")    public SchedulerFactoryBean createSchedulerFactoryBean(){        SchedulerFactoryBean schedulerFactoryBean=new SchedulerFactoryBean();        schedulerFactoryBean.setOverwriteExistingJobs(true);        schedulerFactoryBean.setJobFactory(jobFactory);        return schedulerFactoryBean;    }    @Bean    public JobDetailImpl createJobDetailsImpl(){        return new JobDetailImpl();    }}
6、 构造Scheduler使用我们自己创建的SchedulerFactoryBean


@PostConstruct    public void registryOutlier(){
outlierServiceImpl.addObserver(this);
scheduler = schedulerFactoryBean.getScheduler();
}

到现在为止。这样在Job里面就可以注入Spring的bean类了。并且将线程的处理逻辑分开。线程负责调度和逻辑的跳转。









推荐阅读
  • Spring框架的核心组件与架构解析 ... [详细]
  • 在使用SSH框架进行项目开发时,经常会遇到一些常见的问题。例如,在Spring配置文件中配置AOP事务声明后,进行单元测试时可能会出现“No Hibernate Session bound to thread”的错误。本文将详细探讨这一问题的原因,并提供有效的解决方案,帮助开发者顺利解决此类问题。 ... [详细]
  • 设计实战 | 10个Kotlin项目深度解析:首页模块开发详解
    设计实战 | 10个Kotlin项目深度解析:首页模块开发详解 ... [详细]
  • 本文作为“实现简易版Spring系列”的第五篇,继前文深入探讨了Spring框架的核心技术之一——控制反转(IoC)之后,将重点转向另一个关键技术——面向切面编程(AOP)。对于使用Spring框架进行开发的开发者来说,AOP是一个不可或缺的概念。了解AOP的背景及其基本原理,对于掌握这一技术至关重要。本文将通过具体示例,详细解析AOP的实现机制,帮助读者更好地理解和应用这一技术。 ... [详细]
  • 本文探讨了如何利用 jQuery 的 JSONP 技术实现跨域调用外部 Web 服务。通过详细解析 JSONP 的工作原理及其在 jQuery 中的应用,本文提供了实用的代码示例和最佳实践,帮助开发者解决跨域请求中的常见问题。 ... [详细]
  • 本文总结了JavaScript的核心知识点和实用技巧,涵盖了变量声明、DOM操作、事件处理等重要方面。例如,通过`event.srcElement`获取触发事件的元素,并使用`alert`显示其HTML结构;利用`innerText`和`innerHTML`属性分别设置和获取文本内容及HTML内容。此外,还介绍了如何在表单中动态生成和操作``元素,以便更好地处理用户输入。这些技巧对于提升前端开发效率和代码质量具有重要意义。 ... [详细]
  • 本文探讨了 Java 中 Pair 类的历史与现状。虽然 Java 标准库中没有内置的 Pair 类,但社区和第三方库提供了多种实现方式,如 Apache Commons 的 Pair 类和 JavaFX 的 javafx.util.Pair 类。这些实现为需要处理成对数据的开发者提供了便利。此外,文章还讨论了为何标准库未包含 Pair 类的原因,以及在现代 Java 开发中使用 Pair 类的最佳实践。 ... [详细]
  • 本文详细探讨了Java事件处理机制的核心概念与实现原理,内容浅显易懂,适合初学者逐步掌握。通过具体的示例和详细的解释,读者可以深入了解Java事件模型的工作方式及其在实际开发中的应用。 ... [详细]
  • 在前文探讨了Spring如何为特定的bean选择合适的通知器后,本文将进一步深入分析Spring AOP框架中代理对象的生成机制。具体而言,我们将详细解析如何通过代理技术将通知器(Advisor)中包含的通知(Advice)应用到目标bean上,以实现切面编程的核心功能。 ... [详细]
  • SQLite数据库CRUD操作实例分析与应用
    本文通过分析和实例演示了SQLite数据库中的CRUD(创建、读取、更新和删除)操作,详细介绍了如何在Java环境中使用Person实体类进行数据库操作。文章首先阐述了SQLite数据库的基本概念及其在移动应用开发中的重要性,然后通过具体的代码示例,逐步展示了如何实现对Person实体类的增删改查功能。此外,还讨论了常见错误及其解决方法,为开发者提供了实用的参考和指导。 ... [详细]
  • 本文深入探讨了CGLIB BeanCopier在Bean对象复制中的应用及其优化技巧。相较于Spring的BeanUtils和Apache的BeanUtils,CGLIB BeanCopier在性能上具有显著优势。通过详细分析其内部机制和使用场景,本文提供了多种优化方法,帮助开发者在实际项目中更高效地利用这一工具。此外,文章还讨论了CGLIB BeanCopier在复杂对象结构和大规模数据处理中的表现,为读者提供了实用的参考和建议。 ... [详细]
  • 掌握Android UI设计:利用ZoomControls实现图片缩放功能
    本文介绍了如何在Android应用中通过使用ZoomControls组件来实现图片的缩放功能。ZoomControls提供了一种简单且直观的方式,让用户可以通过点击放大和缩小按钮来调整图片的显示大小。文章详细讲解了ZoomControls的基本用法、布局设置以及与ImageView的结合使用方法,适合初学者快速掌握Android UI设计中的这一重要功能。 ... [详细]
  • 本项目在Java Maven框架下,利用POI库实现了Excel数据的高效导入与导出功能。通过优化数据处理流程,提升了数据操作的性能和稳定性。项目已发布至GitHub,当前最新版本为0.0.5。该项目不仅适用于小型应用,也可扩展用于大型企业级系统,提供了灵活的数据管理解决方案。GitHub地址:https://github.com/83945105/holygrail,Maven坐标:`com.github.83945105:holygrail:0.0.5`。 ... [详细]
  • Spring框架入门指南:专为新手打造的详细学习笔记
    Spring框架是Java Web开发中广泛应用的轻量级应用框架,以其卓越的功能和出色的性能赢得了广大开发者的青睐。本文为初学者提供了详尽的学习指南,涵盖基础概念、核心组件及实际应用案例,帮助新手快速掌握Spring框架的核心技术与实践技巧。 ... [详细]
  • 在近期的项目开发过程中,ORM层采用了MyBatis,并且需要连接多个数据库,这带来了多数据源配置的挑战。为了解决这一问题,我们可以通过巧妙运用注解来实现优雅的数据源切换,确保系统的灵活性和可维护性。这种方法不仅简化了配置,还提高了代码的可读性和扩展性。 ... [详细]
author-avatar
feloveyu
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有