序文: クォーツはタイミング スケジューリング フレームワークです。現在の市場に関する限り、実際にはクォーツよりも優れたタイミング スケジューリング フレームワークがいくつかあります。それらはクォーツよりも優れたパフォーマンスを備えているだけでなく、学習コストが低く、また、タイミングタスクの視覚的な操作。たとえば、xxl-Job と elastic-Job は、現在仕事で最も一般的に使用されているタイミング スケジューリング フレームワークであり、分散プロジェクトに適しており、優れたパフォーマンスを備えています。これは多くの人が混乱する質問ですが、この場合、なぜクォーツを理解して学ぶ必要があるのでしょうか? 私個人としては、Quartz の学習には 2 つの側面があると考えています。1 つは、フレームワーク xxl-Job と elastic-Job は Quartz に基づいて開発されており、Quartz を学習することは、タイミング スケジューリングの理解に役立ちます。2 番目の側面は作業要件です。伝統的なインターネット企業の中には、スケジュールされたタスクの開発を完了するために Quartz を使用するプロジェクトがまだ多くあります。Quartz を理解していなければ、たとえ書くように頼んでも上司は対応できません。スケジュールされたタスク。
1. クォーツの基本概念
上の図からわかるように、ジョブは複数の jobDetails に対してカプセル化でき、jobDetail はトリガーのルールを設定できますが、トリガーに装備できる jobDetail は 1 つだけです。
スケジューラ: スケジュールされたタスクの作業コンテナまたはワークプレイスとして理解でき、すべてのスケジュールされたタスクがその中に配置され、開始および停止できます。
トリガー: スケジュールされたタスクの作業ルール構成として理解でき、たとえば、数分ごとに呼び出すことも、毎日指定した時刻に実行することもできます。
jobDetail : スケジュールされたタスクの情報 (スケジュールされたタスク、グループなどの名前の構成など)。
job : スケジュールされたタスクの実際のビジネス処理ロジック。
2. クォーツのシンプルな使い方
これは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();
}
}
}
3. クォーツとスプリングブーツの統合と使用
公式サイトでも紹介されていますが、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 を使用してスケジュールされたタスクを実装するには、まず新しいジョブを作成する必要があります。Springboot では、新しいジョブ クラスは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 を作成してスケジュールされたタスクを開始します。これを実現するには 2 つの方法があります。基本的には、jobDetail と Trigger を作成します。
方法 1: 対応するジョブの JobDetail と Trigger を作成します。この方法で注意する点は 2 つあります。JobDetail はpersist.storeDurively() に設定する必要があります。トリガーの作成には .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;
}
}
方法 2: Bean を注入する前に JobDetail と Trigger を初期化して作成し、スケジューラを使用してそれらを呼び出します。これはネイティブ 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);
}
}
4. クォーツの持続性
Quartz の永続化には 2 種類のストレージがあり、通常は Quartz 関連のテーブルとビジネス テーブルが同じデータベースに配置されます。ただし、パフォーマンスの問題を考慮する場合は、ビジネス テーブルを別のデータベースに、Quartz 関連のテーブルを 1 つのデータベースに配置して、複数のデータ ソースを構成する必要があります。
https://docs.spring.io/spring-boot/docs/2.3.12.RELEASE/reference/html/spring-boot-features.html#boot-features-quartz
Spring の公式 Web サイトには、デフォルトでインメモリ JobStore が使用されると記載されています。ただし、アプリケーションに DataSourcebean があり、spring.quartz が使用可能な場合は、JDBC ベースのストレージを構成できます。ジョブ ストレージ タイプのプロパティはそれに応じて構成されます。2 番目の構成は、テーブル データを削除し、テーブルを起動するたびに再作成することです (実際の運用環境では、DML を使用して手動でテーブルを作成することを好み、この値は Never に設定されます)。Quartz jar パッケージでは、このパスの下にさまざまなデータベースの dml があります: org.quartz.impl.jdbcjobstore
spring.quartz.job-store-type =jdbc
spring.quartz.jdbc.initialize-schema =never
別の方法:
Quartz にアプリケーションのメイン DataSource の代わりに DataSource を使用させるには、DataSource Bean を宣言し、その @bean メソッドに @QuartzDataSource アノテーションを付けます。これを行うと、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;
}
}
もう一つ注意点があります。Springコンテナにジョブビーンを注入すると、次回からジョブビーンを注入する必要がないので、@Componentをコメントアウトします。
5. クォーツの不発戦略
**誤射:** タスクのトリガー時間に達しましたが、タスクはトリガーされません。
理由: - @DisallowConcurrentExecution アノテーションを使用し、タスク実行時間 > タスク間隔を使用します。
- スレッド プールがいっぱいで、タスクを実行するためのリソースがありません。
- マシンがダウンしているか、停止していると考えられますが、できるだけ早く動作を再開します。
@DisallowConcurrentExecution : これは一般的に使用されるアノテーションで、前のタスクが実行された後に次のタスクが実行され、タスクの並列実行が許可されていないことを証明します。
@PersistJobDataAfterExecution : タスクの実行後、データは次の実行まで保持されます。
異なる ScheduleBuilder、SimpleScheduleBuilder、および非 SimpleScheduleBuilder に対して、異なる火災戦略を設定できます。
SimpleScheduleBuilder は 6 種類、SimpleScheduleBuilder は 3 種類ありますが、実際の作業では CronScheduleBuilder を使用します。
.withMisfireHandlingstructIgnoreMisfires()
MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY = -1
トリガーされていない実行はすべてただちに実行され、トリガーはスケジュールどおりに実行されます。
.withMisfireHandlingstructFireAndProceed()
MISFIRE_INSTRUCTION_FIRE_ONCE_NOW = 1
最初の誤った実行をただちに実行し、他の実行を破棄します (つまり、すべての誤った実行はマージされます)。つまり、トリガーの実行が何回失敗しても、それらは 1 回だけ実行され、その後トリガーが実行されます。その後、スケジュールどおりに実行されます。(デフォルトの不点火戦略)
.withMisfireHandlingstructionDoNothing()
MISFIRE_INSTRUCTION_DO_NOTHING = 2
トリガーされていない実行はすべて、トリガーの次のスケジューリング サイクルが計画どおりに実行される前に破棄されます。
6. まとめ
クォーツに関するもう 1 つの非常に重要なポイントは、コーンの表現です。個人的には、丸暗記する必要はないと思いますが、書き方が本当に分からない場合は、コーンの表現をオンラインで見つけて、オンラインで変換するだけで済みます。
単純なデモのコード アドレス: https://gitee.com/gorylee/quartz-demo
本番プロジェクトでの Quartz の構成使用コード アドレス: https://gitee.com/gorylee/learnDemo/tree/master/quartzDemo