前言:quartz是一个定时调度的框架,就目前市场上来说,其实有比quartz更优秀的一些定时调度框架,不但性能比quartz好,学习成本更低,而且还提供可视化操作定时任务。例如xxl-Job,elastic-Job这两个算是目前工作中使用比较多的定时调度框架了,适配于分布式的项目,性能也是很优秀。这是很多人就很疑惑,既然这样我们为什么还要了解学习quartz呢?我个人觉得学习quartz有两方面,首先xxl-Job,elastic-Job这些框架都是基于quartz的基础上二次开发的,学习quartz更有利于我们加强理解定时调度。第二方面就是工作需求,有一些传统互联网公司还是有很多项目是使用quartz来完成定时任务的开发的,不懂quartz的话,老板叫你写个定时任务都搞不定。

有上图可以看到,一个job可以给多个jobDetail封装,一个jobDetail可以给trigger来配置规则,但是一个trigger只能装配一个jobDetail。
scheduler:可以理解为定时任务的工作容器或者说是工作场所,所有定时任务都是放在里面工作,可以开启和停止。
trigger:可以理解为是定时任务任务的工作规则配置,例如说,没个几分钟调用一次,或者说指定每天那个时间点执行。
jobDetail:定时任务的信息,例如配置定时任务的名字,群组之类的。
job:定时任务的真正的业务处理逻辑的地方。
这是quartz的api使用,在官网直接提供使用例子,但是在工作中用不到这种方式的
地址:https://www.quartz-scheduler.org/documentation/quartz-2.3.0/quick-start.html
public class QuartzTest {
public static void main(String[] args) throws Exception{
try {
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
scheduler.start();
JobDetail job = newJob(HelloJob.class)
.withIdentity("job1", "group1")
.build();
Trigger trigger = newTrigger()
.withIdentity("trigger1", "group1")
.startNow()
.withSchedule(simpleSchedule()
.withIntervalInSeconds(2)
.repeatForever())
.build();
scheduler.scheduleJob(job, trigger);
TimeUnit.SECONDS.sleep(20);
scheduler.shutdown();
} catch (SchedulerException se) {
se.printStackTrace();
}
}
}
在官网中介绍了,只要你引用了quartz的依赖后,springboot会自适配调度器。当然我们也可以新建bean,修改SchedulerFactoryBean的一些默认属性值。
使用javaBean方式按实际业务需求初始化SchedulerFactoryBean(可以不要,就用默认SchedulerFactoryBean
@Configuration
public class QuartzConfiguration {
// Quartz配置文件路径
private static final String QUARTZ_CONFIG = "config/quartz.properties";
@Value("${task.enabled:true}")
private boolean enabled;
@Autowired
private DataSource dataSource;
@Bean
public SchedulerFactoryBean schedulerFactoryBean() {
SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
schedulerFactoryBean.setDataSource(dataSource);
// 设置加载的配置文件
schedulerFactoryBean.setConfigLocation(new ClassPathResource(QUARTZ_CONFIG));
// 用于quartz集群,QuartzScheduler 启动时更新己存在的Job
schedulerFactoryBean.setOverwriteExistingJobs(true);
schedulerFactoryBean.setStartupDelay(5);// 系统启动后,延迟5s后启动定时任务,默认为0
// 启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了
schedulerFactoryBean.setOverwriteExistingJobs(true);
// SchedulerFactoryBean在初始化后是否马上启动Scheduler,默认为true。如果设置为false,需要手工启动Scheduler
schedulerFactoryBean.setAutoStartup(enabled);
return schedulerFactoryBean;
}
}
要使用quartz实现定时任务,首先要新建一个Job,在springboot中,新建的Job类要继承QuartzJobBean
public class HelloJob extends QuartzJobBean {
@Override
protected void executeInternal(JobExecutionContext context) {
StringJoiner joiner = new StringJoiner(" | ")
.add("---HelloJob---")
.add(context.getTrigger().getKey().getName())
.add(DateUtil.formatDate(new Date()));
System.out.println(joiner);
}
}
创建jobDetail和Trigger来启动定时任务,有两种方式可以实现,本质上就是创建jobDetail和Trigger
方式一:为对应的Job创建JobDetail和Trigger,这种方式有两个注意的地方,jobDetail一定要设置为可持久化.storeDurably(),Trigger创建要用.forJob(“helloJob”),要与JobDetail定义的相同。
@Component
public class HelloJobDetailConfig {
@Bean
public JobDetail helloJobDetail(){
JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)
.withIdentity("helloJob")
.storeDurably()
.usingJobData("data", "保密信息")
.build();
return jobDetail;
}
@Bean
public Trigger helloJobTrigger(){
Trigger trigger = TriggerBuilder.newTrigger()
.forJob("helloJob")
.withSchedule(simpleSchedule()
.withIntervalInSeconds(3)
.repeatForever())
.build();
return trigger;
}
}
方式二:在注入Bean之前初始化创建JobDetail和Trigger,然后使用Scheduler来调用,跟原生API调用差不多。
@Component
public class HelloJobDetailConfig2 {
@Autowired
private Scheduler scheduler;
@PostConstruct
protected void InitHelloJob() throws Exception {
JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)
.withIdentity("helloJob")
// .storeDurably()
.usingJobData("data", "保密信息")
.build();
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("helloTrigger")
.withSchedule(simpleSchedule()
.withIntervalInSeconds(3)
.repeatForever())
.build();
scheduler.scheduleJob(jobDetail,trigger);
}
}

quartz持久化有两种存储,一般情况下quartz相关的表和业务表是放在同一个数据库里的。但是如果考虑性能问题的话,就要配多数据源,业务表单独一个库,quartz相关的表放在一个库。
https://docs.spring.io/spring-boot/docs/2.3.12.RELEASE/reference/html/spring-boot-features.html#boot-features-quartz
spring官网说明,默认情况下,使用内存中的JobStore。但是,如果应用程序中有DataSourcebean,并且spring.quartz是可用的,则可以配置基于JDBC的存储。将相应地配置作业存储类型属性。第二个配置,每次启动先删除表数据再重新创建(在实际生产中,个人更倾向于拿dml来手动创建表,这个值设置为never)。在quartz的jar包里这个路径下有不同数据库的dml:org.quartz.impl.jdbcjobstore
spring.quartz.job-store-type=jdbc
spring.quartz.jdbc.initialize-schema=never
另外一种方式:
要让Quartz使用DataSource而不是应用程序的主DataSource,请声明DataSourcebean,并用@QuartzDataSource注释其@bean方法。这样做可以确保SchedulerFactoryBean和模式初始化都使用Quartz特定的DataSource
@Configuration
public class QuartzDataSourceConfig {
@Bean
@QuartzDataSource
public DataSource quartzDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setUsername("root");
dataSource.setPassword("123456");
dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/quartz?useUnicode=true&characterEncoding=utf-8&useSSL=false");
return dataSource;
}
}
还有一点需要注意:当jobbean已经注入spring容器后,下次不用需要再注入,把@Component注释掉。
**misfire:**到了任务触发时间点,但是任务没有被触发
原因:- 使用@DisallowConcurrentExecution注解,而且任务的执行时间>任务间隔
-线程池满了,没有资源执行任务
-机器宕机或者认为停止,果断时间恢复运行。
@DisallowConcurrentExecution:这个是比较常用的注解,证上一个任务执行完后,再去执行下一个任务,不会允许任务并行执行。
@PersistJobDataAfterExecution:任务执行完后,会持久化保留数据到下次 执行
针对不同的ScheduleBuilder,可以设置不同的失火策略,SimpleScheduleBuilder和非SimpleScheduleBuilder,
SimpleScheduleBuilder有六种,而非SimpleScheduleBuilder有三种,在实际工作中我们使用的比较的是CronScheduleBuilder.
.withMisfireHandlingInstructionIgnoreMisfires()
MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY = -1
所有未触发的执行都会立即执行,然后触发器再按计划运行。
.withMisfireHandlingInstructionFireAndProceed()
MISFIRE_INSTRUCTION_FIRE_ONCE_NOW = 1
立即执行第一个错误的执行并丢弃其他(即所有错误的执行合并在一起),也就是说无论错过了多少次触发器的执行,都只会立即执行一次,然后触发器再按计划运行。(默认的失火策略)
.withMisfireHandlingInstructionDoNothing()
MISFIRE_INSTRUCTION_DO_NOTHING = 2
所有未触发的执行都将被丢弃,然后再触发器的下一个调度周期按计划运行。
关于quartz还有一个很重要的点就是corn表达式,这个个人认为没必要死记硬背,实在不会写的,上网找corn表达式在线转换就可以了。
一个简单demo的代码地址:https://gitee.com/gorylee/quartz-demo
生产项目中quartz的配置使用代码地址:https://gitee.com/gorylee/learnDemo/tree/master/quartzDemo
我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div
我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看rubyzip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d
类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc
很好奇,就使用rubyonrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提
假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于
我正在尝试使用ruby和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我
关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。
我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t
我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h
我想为Heroku构建一个Rails3应用程序。他们使用Postgres作为他们的数据库,所以我通过MacPorts安装了postgres9.0。现在我需要一个postgresgem并且共识是出于性能原因你想要pggem。但是我对我得到的错误感到非常困惑当我尝试在rvm下通过geminstall安装pg时。我已经非常明确地指定了所有postgres目录的位置可以找到但仍然无法完成安装:$envARCHFLAGS='-archx86_64'geminstallpg--\--with-pg-config=/opt/local/var/db/postgresql90/defaultdb/po