【SpringBoot】调度和执行定时任务--Quartz(超详细)
Quartz 是一个功能强大的任务调度框架,广泛用于在 Java 应用程序中定时执行任务,同时它支持 Cron 表达式、持久化任务、集群等特性。以下是 Quartz 的详细使用教程,包括安装、基本概念、简单示例和高级功能。
1. 安装 Quartz
首先,在你的项目中添加 Quartz 依赖。对于 Maven 项目,可以在 pom.xml
中添加以下依赖:
<dependency><groupId>org.quartz-scheduler</groupId><artifactId>quartz</artifactId><version>2.3.2</version>
</dependency>
对于 Gradle 项目,可以在 build.gradle
中添加以下依赖:
implementation 'org.quartz-scheduler:quartz:2.3.2'
2. 基本概念
Quartz 的核心概念包括:
- Scheduler:调度器,是 Quartz 的核心,负责管理和调度任务。
- Job:任务,是实际执行的工作单元。需要实现
Job
接口。 - JobDetail:定义任务的详细信息,包括任务的名称、组、以及任务的类。
- Trigger:触发器,定义任务何时执行。常用的触发器包括 SimpleTrigger 和 CronTrigger。
- JobStore:任务存储,定义任务的存储方式。常见的有 RAMJobStore(内存存储)和 JDBCJobStore(数据库存储)。
3. 简单示例
以下是一个简单的 Quartz 示例,展示如何创建和调度一个任务。
1. 定义 Job 类
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;public class HelloJob implements Job {@Overridepublic void execute(JobExecutionContext context) throws JobExecutionException {System.out.println("Hello, Quartz! Current time: " + System.currentTimeMillis());}
}
2. 配置 Scheduler 和 Job
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;public class QuartzExample {public static void main(String[] args) throws SchedulerException {// 创建 Scheduler 实例Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();// 定义一个 JobDetail 实例JobDetail job = JobBuilder.newJob(HelloJob.class).withIdentity("helloJob", "group1").build();// 创建一个触发器,每隔5秒执行一次Trigger trigger = TriggerBuilder.newTrigger().withIdentity("helloTrigger", "group1").startNow().withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(5).repeatForever()).build();// 调度任务scheduler.start();scheduler.scheduleJob(job, trigger);}
}
4. 高级功能
1. 使用 CronTrigger
CronTrigger 允许使用 Cron 表达式来定义复杂的调度规则。
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;public class CronTriggerExample {public static void main(String[] args) throws SchedulerException {Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();JobDetail job = JobBuilder.newJob(HelloJob.class).withIdentity("cronJob", "group1").build();// 使用 Cron 表达式创建触发器Trigger trigger = TriggerBuilder.newTrigger().withIdentity("cronTrigger", "group1").withSchedule(CronScheduleBuilder.cronSchedule("0/10 * * * * ?")).build();scheduler.start();scheduler.scheduleJob(job, trigger);}
}
2. Cron 表达式
Cron 表达式用于定义任务调度的时间规则。它由6或7个字段组成,字段之间用空格分隔。以下是每个字段的含义:
┌───────────── 秒 (0 - 59)
│ ┌───────────── 分 (0 - 59)
│ │ ┌───────────── 小时 (0 - 23)
│ │ │ ┌───────────── 日 (1 - 31)
│ │ │ │ ┌───────────── 月 (1 - 12)
│ │ │ │ │ ┌───────────── 星期几 (0 - 7) (0 和 7 都是星期日)
│ │ │ │ │ │
│ │ │ │ │ │
* * * * * *
2.1 特殊字符
*
: 表示任意值。?
: 仅在日和星期字段中使用,表示不指定值。-
: 表示范围,例如10-12
表示从10到12。,
: 表示列表值,例如1,2,3
表示1、2、3。/
: 表示增量,例如0/15
表示从0开始每15分钟。L
: 表示最后,例如L
在日字段表示月的最后一天。W
: 表示最近的工作日,例如15W
表示最接近15号的工作日。#
: 表示第几个星期几,例如2#1
表示第一个星期一。
2.2 示例
0 0 12 * * ?
: 每天中午12点执行。0 15 10 ? * *
: 每天上午10:15执行。0 15 10 * * ?
: 每天上午10:15执行。0 15 10 * * ? 2024
: 2024年每天上午10:15执行。0 * 14 * * ?
: 每天下午2点到2:59每分钟执行一次。0 0/5 14 * * ?
: 每天下午2点到2:55每5分钟执行一次。0 0/5 14,18 * * ?
: 每天下午2点到2:55每5分钟执行一次,以及每天下午6点到6:55每5分钟执行一次。0 0-5 14 * * ?
: 每天下午2点到2:05每分钟执行一次。0 10,44 14 ? 3 WED
: 每年三月的每个星期三下午2:10和2:44执行。
3. 使用 JobListener
JobListener 可以在任务执行的不同阶段进行拦截和处理。
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobListener;public class MyJobListener implements JobListener {@Overridepublic String getName() {return "MyJobListener";}@Overridepublic void jobToBeExecuted(JobExecutionContext context) {//在任务即将被执行时调用,可以在任务执行前进行一些准备工作或记录日志。System.out.println("Job is about to be executed: " + context.getJobDetail().getKey());}@Overridepublic void jobExecutionVetoed(JobExecutionContext context) {//在任务执行被否决时调用,当某些条件满足时,可以阻止任务的执行,并在此方法中执行相应的处理逻辑。System.out.println("Job execution was vetoed: " + context.getJobDetail().getKey());}@Overridepublic void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) {//在任务执行完成后调用,可以在任务执行后进行一些清理工作或记录日志。如果任务执行过程中抛出异常,jobException 将包含该异常信息。System.out.println("Job was executed: " + context.getJobDetail().getKey());if (jobException != null) {System.out.println("Job encountered an exception: " + jobException.getMessage());}}
}
在 Scheduler 中注册 JobListener:
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;public class JobListenerExample {public static void main(String[] args) throws SchedulerException {Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();//创建任务JobDetail job = JobBuilder.newJob(HelloJob.class).withIdentity("listenerJob", "group1").build();创建触发器Trigger trigger = TriggerBuilder.newTrigger().withIdentity("listenerTrigger", "group1").startNow().withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(5).repeatForever()).build();// 创建并注册 JobListenerMyJobListener listener = new MyJobListener();scheduler.getListenerManager().addJobListener(listener);scheduler.start();scheduler.scheduleJob(job, trigger);}
}
5. 持久化支持
Quartz 支持将任务数据持久化到数据库中,以便在系统重启后恢复任务状态。可以使用 JDBCJobStore 来实现这一点。
在 quartz.properties
文件中配置 JDBCJobStore:
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.dataSource = myDS
org.quartz.jobStore.tablePrefix = QRTZ_org.quartz.dataSource.myDS.driver = com.mysql.cj.jdbc.Driver
org.quartz.dataSource.myDS.URL = jdbc:mysql://localhost:3306/quartz
org.quartz.dataSource.myDS.user = root
org.quartz.dataSource.myDS.password = password
org.quartz.dataSource.myDS.maxConnections = 5
6. 并发控制
在 Quartz 中,默认情况下,任务是并发执行的。如果需要确保同一个任务实例不被并发执行,可以实现 DisallowConcurrentExecution
接口。为了清楚地展示 DisallowConcurrentExecution
注解的作用,我们可以创建两个示例:一个使用 DisallowConcurrentExecution
注解,另一个不使用该注解。通过这两个示例,我们可以观察到任务在并发执行方面的差异。
示例 1:不使用 DisallowConcurrentExecution
在这个示例中,我们创建一个简单的任务类,它会模拟一个长时间运行的任务,并且不使用 DisallowConcurrentExecution
注解。
1. 定义 Job 类
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;public class ConcurrentJob implements Job {@Overridepublic void execute(JobExecutionContext context) throws JobExecutionException {System.out.println("ConcurrentJob is executing at: " + System.currentTimeMillis());try {// 模拟长时间运行的任务Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("ConcurrentJob finished at: " + System.currentTimeMillis());}
}
2. 配置 Scheduler 和 Job
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;public class ConcurrentJobExample {public static void main(String[] args) throws SchedulerException {Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();JobDetail job = JobBuilder.newJob(ConcurrentJob.class).withIdentity("concurrentJob", "group1").build();// 创建一个触发器,每隔2秒执行一次Trigger trigger = TriggerBuilder.newTrigger().withIdentity("concurrentTrigger", "group1").startNow().withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(2).repeatForever()).build();scheduler.start();scheduler.scheduleJob(job, trigger);}
}
示例 2:使用 DisallowConcurrentExecution
在这个示例中,我们创建一个类似的任务类,但这次使用 DisallowConcurrentExecution
注解,确保任务不会并发执行。
1. 定义 Job 类
import org.quartz.DisallowConcurrentExecution;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;@DisallowConcurrentExecution
public class NonConcurrentJob implements Job {@Overridepublic void execute(JobExecutionContext context) throws JobExecutionException {System.out.println("NonConcurrentJob is executing at: " + System.currentTimeMillis());try {// 模拟长时间运行的任务Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("NonConcurrentJob finished at: " + System.currentTimeMillis());}
}
2. 配置 Scheduler 和 Job
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;public class NonConcurrentJobExample {public static void main(String[] args) throws SchedulerException {Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();JobDetail job = JobBuilder.newJob(NonConcurrentJob.class).withIdentity("nonConcurrentJob", "group1").build();// 创建一个触发器,每隔2秒执行一次Trigger trigger = TriggerBuilder.newTrigger().withIdentity("nonConcurrentTrigger", "group1").startNow().withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(2).repeatForever()).build();scheduler.start();scheduler.scheduleJob(job, trigger);}
}
运行示例
当你运行这两个示例时,你会看到以下行为:
不使用 DisallowConcurrentExecution
的输出
ConcurrentJob is executing at: 1633017600000
ConcurrentJob is executing at: 1633017602000
ConcurrentJob finished at: 1633017605000
ConcurrentJob is executing at: 1633017604000
ConcurrentJob finished at: 1633017607000
ConcurrentJob finished at: 1633017609000
可以看到,任务是并发执行的,多个任务实例同时运行。
使用 DisallowConcurrentExecution
的输出
NonConcurrentJob is executing at: 1633017600000
NonConcurrentJob finished at: 1633017605000
NonConcurrentJob is executing at: 1633017605000
NonConcurrentJob finished at: 1633017610000
NonConcurrentJob is executing at: 1633017610000
NonConcurrentJob finished at: 1633017615000
可以看到,任务是顺序执行的,新的任务实例只有在前一个实例完成后才会开始执行。
import org.quartz.DisallowConcurrentExecution;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;@DisallowConcurrentExecution
public class NonConcurrentJob implements Job {@Overridepublic void execute(JobExecutionContext context) throws JobExecutionException {System.out.println("Non-concurrent job is executing at: " + System.currentTimeMillis());}
}
通过这两个示例,我们可以清楚地看到 DisallowConcurrentExecution
注解的作用:它确保同一个任务实例不会并发执行,而是顺序执行。这在某些需要严格控制任务执行顺序的场景中非常有用。
7. 任务依赖管理
Quartz 本身并不直接支持任务依赖管理(即任务之间的依赖关系),但可以通过编程方式实现这一功能。通常的方法是使用多个 Job 和 Trigger,并在 Job 执行完成后手动调度下一个依赖的 Job。
1. 异步任务依赖管理
Quartz 本身并不直接支持任务依赖管理(即任务之间的依赖关系),但可以通过编程方式实现这一功能。通常的方法是使用多个 Job 和 Trigger,并在 Job 执行完成后手动调度下一个依赖的 Job。
以下是一个示例,展示如何在 Quartz 中实现任务依赖管理:
示例场景
假设有三个任务:
- 任务 A:完成后触发任务 B。
- 任务 B:完成后触发任务 C。
- 任务 C:最后执行。
实现步骤
- 定义三个 Job 类。
- 配置 Scheduler 和 Job。
- 在每个 Job 中执行完成后手动调度下一个依赖的 Job。
代码示例
1. 定义 Job 类
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;public class JobA implements Job {@Overridepublic void execute(JobExecutionContext context) throws JobExecutionException {System.out.println("Job A executed at: " + System.currentTimeMillis());// 获取 Scheduler 实例Scheduler scheduler = context.getScheduler();try {// 创建 JobB 的 JobDetailJobDetail jobB = JobBuilder.newJob(JobB.class).withIdentity("jobB", "group1").build();// 创建立即触发的 TriggerTrigger triggerB = TriggerBuilder.newTrigger().withIdentity("triggerB", "group1").startNow().build();// 调度 JobBscheduler.scheduleJob(jobB, triggerB);} catch (SchedulerException e) {throw new JobExecutionException(e);}}
}public class JobB implements Job {@Overridepublic void execute(JobExecutionContext context) throws JobExecutionException {System.out.println("Job B executed at: " + System.currentTimeMillis());// 获取 Scheduler 实例Scheduler scheduler = context.getScheduler();try {// 创建 JobC 的 JobDetailJobDetail jobC = JobBuilder.newJob(JobC.class).withIdentity("jobC", "group1").build();// 创建立即触发的 TriggerTrigger triggerC = TriggerBuilder.newTrigger().withIdentity("triggerC", "group1").startNow().build();// 调度 JobCscheduler.scheduleJob(jobC, triggerC);} catch (SchedulerException e) {throw new JobExecutionException(e);}}
}public class JobC implements Job {@Overridepublic void execute(JobExecutionContext context) throws JobExecutionException {System.out.println("Job C executed at: " + System.currentTimeMillis());}
}
2. 配置 Scheduler 和 Job
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;public class QuartzDependencyExample {public static void main(String[] args) throws SchedulerException {// 创建 Scheduler 实例Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();// 定义 JobAJobDetail jobA = JobBuilder.newJob(JobA.class).withIdentity("jobA", "group1").build();// 创建立即触发的 TriggerTrigger triggerA = TriggerBuilder.newTrigger().withIdentity("triggerA", "group1").startNow().build();// 调度 JobAscheduler.start();scheduler.scheduleJob(jobA, triggerA);}
}
解释
- JobA:在执行完成后,手动调度 JobB。
- JobB:在执行完成后,手动调度 JobC。
- JobC:最后执行,没有后续任务。
通过这种方式,可以实现任务之间的依赖关系。在每个任务执行完成后,手动调度下一个依赖的任务。这样,即使 Quartz 本身不直接支持任务依赖管理,也可以通过编程方式实现这一功能。
注意事项
- 异常处理:确保在调度下一个任务时正确处理异常,防止由于调度失败导致任务链中断。
- 并发控制:如果有多个任务链,需要确保并发控制,防止多个任务同时调度同一个任务。
- 持久化支持:如果任务链较长或需要持久化,确保使用持久化的 Scheduler 配置,以便在系统重启后恢复任务状态。
2. 同步任务依赖管理
在上面的示例中,任务之间的调度是异步的。每个 Job 在执行完成后,会立即调度下一个 Job,但不会等待下一个 Job 的执行完成。这种方式适用于大多数场景,但如果需要同步执行(即一个任务必须等待前一个任务执行完成后再执行),则需要一些额外的机制来确保任务的同步执行。
为了实现同步的任务依赖管理,可以使用以下方法:
- 使用 JobListener:监听每个任务的执行完成事件,并在事件触发时调度下一个任务。
- 使用共享的状态或信号量:在任务之间共享状态或使用信号量来确保任务按顺序执行。
以下是一个使用 JobListener
实现同步任务依赖管理的示例:
实现步骤
- 定义三个 Job 类。
- 实现 JobListener接口。
- 配置 Scheduler 和 Job。
- 当触发
jobWasExecuted
后调度下一个依赖的 Job。
示例代码
1. 定义 Job 类
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;public class JobA implements Job {@Overridepublic void execute(JobExecutionContext context) throws JobExecutionException {System.out.println("Job A executed at: " + System.currentTimeMillis());}
}public class JobB implements Job {@Overridepublic void execute(JobExecutionContext context) throws JobExecutionException {System.out.println("Job B executed at: " + System.currentTimeMillis());}
}public class JobC implements Job {@Overridepublic void execute(JobExecutionContext context) throws JobExecutionException {System.out.println("Job C executed at: " + System.currentTimeMillis());}
}
2. 定义 JobListener
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobListener;
import org.quartz.SchedulerException;public class DependencyJobListener implements JobListener {private Scheduler scheduler;public DependencyJobListener(Scheduler scheduler) {this.scheduler = scheduler;}@Overridepublic String getName() {return "DependencyJobListener";}@Overridepublic void jobToBeExecuted(JobExecutionContext context) {// No action needed before job execution}@Overridepublic void jobExecutionVetoed(JobExecutionContext context) {// No action needed if job execution is vetoed}@Overridepublic void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) {String jobName = context.getJobDetail().getKey().getName();try {if ("jobA".equals(jobName)) {// 调度 JobBJobDetail jobB = JobBuilder.newJob(JobB.class).withIdentity("jobB", "group1").build();Trigger triggerB = TriggerBuilder.newTrigger().withIdentity("triggerB", "group1").startNow().build();scheduler.scheduleJob(jobB, triggerB);} else if ("jobB".equals(jobName)) {// 调度 JobCJobDetail jobC = JobBuilder.newJob(JobC.class).withIdentity("jobC", "group1").build();Trigger triggerC = TriggerBuilder.newTrigger().withIdentity("triggerC", "group1").startNow().build();scheduler.scheduleJob(jobC, triggerC);}} catch (SchedulerException e) {e.printStackTrace();}}
}
3. 配置 Scheduler 和 Job
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;public class QuartzDependencyExample {public static void main(String[] args) throws SchedulerException {// 创建 Scheduler 实例Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();// 定义 JobAJobDetail jobA = JobBuilder.newJob(JobA.class).withIdentity("jobA", "group1").build();// 创建立即触发的 TriggerTrigger triggerA = TriggerBuilder.newTrigger().withIdentity("triggerA", "group1").startNow().build();// 添加 JobListenerscheduler.getListenerManager().addJobListener(new DependencyJobListener(scheduler));// 调度 JobAscheduler.start();scheduler.scheduleJob(jobA, triggerA);}
}
解释
- JobA、JobB、JobC:分别定义三个独立的 Job 类。
- DependencyJobListener:实现
JobListener
接口,在每个 Job 执行完成后,根据当前执行的 Job 调度下一个 Job。 - Scheduler 配置:在 Scheduler 中注册
DependencyJobListener
,并调度初始的 JobA。
注意事项
- 异常处理:确保在调度下一个任务时正确处理异常,防止由于调度失败导致任务链中断。
- 并发控制:如果有多个任务链,需要确保并发控制,防止多个任务同时调度同一个任务。
- 持久化支持:如果任务链较长或需要持久化,确保使用持久化的 Scheduler 配置,以便在系统重启后恢复任务状态。
3. 任务拦截
在 Quartz 中,可以通过实现 JobListener
来拦截任务的执行。JobListener
提供了几个方法,可以在任务执行前、执行后以及任务被否决时进行拦截和处理。
下面是一个详细的示例代码,展示如何使用 JobListener
来拦截任务的执行,并附有详细注释。
示例代码
1. 定义 Job 类
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;// 定义一个简单的 Job 类
public class SampleJob implements Job {@Overridepublic void execute(JobExecutionContext context) throws JobExecutionException {System.out.println("SampleJob is executing at: " + System.currentTimeMillis());}
}
2. 实现 JobListener
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobListener;// 实现 JobListener 接口
public class MyJobListener implements JobListener {@Overridepublic String getName() {// 返回监听器的名称return "MyJobListener";}@Overridepublic void jobToBeExecuted(JobExecutionContext context) {// 在任务即将执行时调用System.out.println("Job is about to be executed: " + context.getJobDetail().getKey());}@Overridepublic void jobExecutionVetoed(JobExecutionContext context) {// 在任务被否决时调用System.out.println("Job execution was vetoed: " + context.getJobDetail().getKey());}@Overridepublic void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) {// 在任务执行完成后调用System.out.println("Job was executed: " + context.getJobDetail().getKey());if (jobException != null) {System.out.println("Job encountered an exception: " + jobException.getMessage());}}
}
3. 配置 Scheduler 和 Job
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;public class QuartzJobListenerExample {public static void main(String[] args) throws SchedulerException {// 创建 Scheduler 实例Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();// 定义一个 JobDetail 实例JobDetail job = JobBuilder.newJob(SampleJob.class).withIdentity("sampleJob", "group1").build();// 创建一个触发器Trigger trigger = TriggerBuilder.newTrigger().withIdentity("sampleTrigger", "group1").startNow().build();// 创建并注册 JobListenerMyJobListener listener = new MyJobListener();scheduler.getListenerManager().addJobListener(listener);// 调度任务scheduler.start();scheduler.scheduleJob(job, trigger);}
}
解释
- SampleJob:定义一个简单的 Job 类,包含一个
execute
方法,打印当前时间。 - MyJobListener:实现
JobListener
接口,提供四个方法:getName
:返回监听器的名称。jobToBeExecuted
:在任务即将执行时调用,打印任务的 Key。jobExecutionVetoed
:在任务被否决时调用,打印任务的 Key。jobWasExecuted
:在任务执行完成后调用,打印任务的 Key 和异常信息(如果有)。
- QuartzJobListenerExample:主类,配置 Scheduler 和 Job:
- 创建 Scheduler 实例。
- 定义一个
JobDetail
实例。 - 创建一个触发器。
- 创建并注册
JobListener
实例。 - 调度任务并启动 Scheduler。
注意事项
- 异常处理:在
jobWasExecuted
方法中处理任务执行中的异常,确保异常信息被记录或处理。 - 并发控制:如果有多个任务并发执行,需要确保监听器的实现是线程安全的。
- 持久化支持:在需要持久化任务状态时,确保 Scheduler 配置支持持久化。
相关文章:
【SpringBoot】调度和执行定时任务--Quartz(超详细)
Quartz 是一个功能强大的任务调度框架,广泛用于在 Java 应用程序中定时执行任务,同时它支持 Cron 表达式、持久化任务、集群等特性。以下是 Quartz 的详细使用教程,包括安装、基本概念、简单示例和高级功能。 1. 安装 Quartz 首先ÿ…...

低代码开发平台系统架构概述
概述 织信低代码开发平台(产品全称:织信Informat)是一款集成了应用设计、运行与管理的综合性平台。它提供了丰富的功能模块,帮助用户快速构建、部署和维护应用程序。织信低代码平台通过集成丰富的功能模块,为用户提供…...
源码编译llama.cpp 、ggml 后端启用自定义BLAS加速
源码编译llama.cpp 、ggml 后端启用自定义BLAS加速 我在llama.cpp 官网上提交了我的解决方案:How to setup OpenBlas on windows? #625 GGML 官网 https://github.com/ggerganov/ggml/issues/959 windows on arm 编译 llama.cpp 、ggml 后端启用自定义BLAS加速 …...

glb数据格式
glb数据格式 glb 文件格式只包含一个glb 文件,文件按照二进制存储,占空间小 浏览 浏览glb工具的很多,ccs,3D查看器等都可以,不安装软件的话用下面网页加载就可以,免费 glTF Viewer (donmccurdy.com) glb…...

手语识别系统源码分享
手语识别检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer Vision …...
Oracle 数据库部署与实施
文章目录 1. macOS 上部署 Oracle 数据库通过 Docker 在 macOS 上部署 2. Linux 上部署 Oracle 数据库直接在 Linux 上部署通过 Docker 在 Linux 上部署 3. Windows 上部署 Oracle 数据库4. 使用 Docker 部署 Oracle 数据库前提条件拉取 Oracle 数据库 Docker 镜像运行 Oracle …...
【Python】 ast.literal_eval 与 eval
一、背景 我在在编写管理后台的过程中,遇到一个小问题,是关于用户名的存储和解码。用户名以base64编码的形式存储在 MySQL 数据库中,并且还保留了b这样的形式,具体为什么要这样存我也不知道,可能是因为有些特殊字符无法直接存储。…...

Java 入门指南:JVM(Java虚拟机)垃圾回收机制 —— 新一代垃圾回收器 ZGC 收集器
文章目录 垃圾回收机制垃圾收集器垃圾收集器分类ZGC 收集器ZGC 的性能优势复制算法指针染色读屏障 ZGC 的工作过程Stop-The-World 暂停阶段并发阶段 垃圾回收机制 垃圾回收(Garbage Collection,GC),顾名思义就是释放垃圾占用的空…...

基于 K8S kubernetes 的常见日志收集方案
目录 1、日志对我们来说到底重不重要? 2、常见的日志收集方案 2.1 EFK 2.2 ELK Stack 2.3 ELKfilebeat 2.4 其他方案 2、elasticsearch组件介绍 3、filebeat组件介绍 3.1 filebeat和beat关系 3.2 filebeat是什么? 3.3 Filebeat工作原理 3.4 …...

Unity3D 小案例 像素贪吃蛇 02 蛇的觅食
Unity3D 小案例 像素贪吃蛇 第二期 蛇的觅食 像素贪吃蛇 食物生成 在场景中创建一个 2D 正方形,调整颜色,添加 Tag 并修改为 Food。 然后拖拽到 Assets 文件夹中变成预制体。 创建食物管理器 FoodManager.cs,添加单例,可以设置…...

【sgCreateCallAPIFunction】自定义小工具:敏捷开发→调用接口方法代码生成工具
<template><div :class"$options.name" class"sgDevTool"><sgHead /><div class"sg-container"><div class"sg-start"><div style"margin-bottom: 10px">调用接口方法定义列表</div…...
京东商品详情的 API 探秘与应用
在当今数字化的商业世界中,获取准确而详细的商品信息对于开发者、商家以及消费者都具有至关重要的意义。京东作为国内领先的电商平台之一,提供了丰富的商品资源和强大的 API 接口,让我们能够轻松获取京东商品的详情信息。本文将带你深入了解如…...

功能测试干了三年,快要废了。。。
8年前刚进入到IT行业,到现在学习软件测试的人越来越多,所以在这我想结合自己的一些看法给大家提一些建议。 最近聊到软件测试的行业内卷,越来越多的转行和大学生进入测试行业,导致软件测试已经饱和了,想要获得更好的待…...

【C++】多态的认识和理解
个人主页 文章目录 ⭐一、多态的概念🎄二、多态的定义及实现1.多态的构成2.实现多态的条件3.虚函数的概念4.虚函数的重写和覆盖5.析构函数的重写6.协变7.override和 final关键字8.重载、重写/覆盖、隐藏这三者的区别 🏠三、纯虚函数和抽象类的关系&#…...
linux-安全管理-用户认证
Linux 安全管理:用户认证 一、概述 用户认证是 Linux 安全管理的核心部分,确保系统能够识别并验证合法用户,同时阻止未经授权的访问。Linux 提供了多种用户认证机制,包括用户名和密码认证、基于密钥的认证、双因素认证ÿ…...

webpack5 构建优化方案看这篇就够了!【Node.js进阶】
无论在面试还是内部晋升,webpack 构建优化方案 一直都是非常重要的部分。 webpack5构建加持 一、项目完成目标二、搭建项目1. 安装koa、koa/router (如果已经配置可路过)2. 创建入口文件3. 安装构建依赖4. 在项目根目录添加 .babelrc 文件5. …...

esp32-C2 对接火山引擎实现智能语音(一)
目录 一、火山引擎大模型简介 1)火山引擎网址: 2)首先需要先注册火山引擎账号 3)语音识别——即语音转为文本 一句话识别 流式语音识别 录音文件识别标准版 录音文件识别极速版 4)语音合成——文本转音频 一、火山引擎大模型简介 火山引擎的智能语音技术,基于业界先…...
【MySQL-初级】mysql基础操作(账户、数据库、表的增删查改)
概述 数据备份与恢复 数据库备份:在cmd下 root用户:sudo mysqldump -u root -p Test > Test.sql普通用户:mysqldump -u zzz -p db_name > db_name.sql 数据库恢复 先创建一个空的数据库在cmd下:sudo mysql -u root -p d…...
centos bash脚本一键运行安装go环境
复制到install_go.sh直接bash install_go.sh运行就完了 echo ----------安装go环境 wget https://go.dev/dl/go1.21.13.linux-amd64.tar.gz tar -zxvf go1.21.13.linux-amd64.tar.gzmkdir /srv cp -r go /srv/echo "PATH$PATH:/srv/go/bin ">> ~/.bashrc echo…...

vue2制作高复用页面
记录一下页面搭建记录,利用vue2组件化开发的思想。这个页面适合于大部分信息管理系统~。模板固定,每次使用,直接修改表单表格参数,api接口等。 以上图页面为例,一个基础数据信息页面可以分为,分类ÿ…...

Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动
一、前言说明 在2011版本的gb28181协议中,拉取视频流只要求udp方式,从2016开始要求新增支持tcp被动和tcp主动两种方式,udp理论上会丢包的,所以实际使用过程可能会出现画面花屏的情况,而tcp肯定不丢包,起码…...

dedecms 织梦自定义表单留言增加ajax验证码功能
增加ajax功能模块,用户不点击提交按钮,只要输入框失去焦点,就会提前提示验证码是否正确。 一,模板上增加验证码 <input name"vdcode"id"vdcode" placeholder"请输入验证码" type"text&quo…...
基础测试工具使用经验
背景 vtune,perf, nsight system等基础测试工具,都是用过的,但是没有记录,都逐渐忘了。所以写这篇博客总结记录一下,只要以后发现新的用法,就记得来编辑补充一下 perf 比较基础的用法: 先改这…...
【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)
升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点,但无自动故障转移能力,Master宕机后需人工切换,期间消息可能无法读取。Slave仅存储数据,无法主动升级为Master响应请求ÿ…...

项目部署到Linux上时遇到的错误(Redis,MySQL,无法正确连接,地址占用问题)
Redis无法正确连接 在运行jar包时出现了这样的错误 查询得知问题核心在于Redis连接失败,具体原因是客户端发送了密码认证请求,但Redis服务器未设置密码 1.为Redis设置密码(匹配客户端配置) 步骤: 1).修…...

AI书签管理工具开发全记录(十九):嵌入资源处理
1.前言 📝 在上一篇文章中,我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源,方便后续将资源打包到一个可执行文件中。 2.embed介绍 🎯 Go 1.16 引入了革命性的 embed 包,彻底改变了静态资源管理的…...
Mysql8 忘记密码重置,以及问题解决
1.使用免密登录 找到配置MySQL文件,我的文件路径是/etc/mysql/my.cnf,有的人的是/etc/mysql/mysql.cnf 在里最后加入 skip-grant-tables重启MySQL服务 service mysql restartShutting down MySQL… SUCCESS! Starting MySQL… SUCCESS! 重启成功 2.登…...

嵌入式学习之系统编程(九)OSI模型、TCP/IP模型、UDP协议网络相关编程(6.3)
目录 一、网络编程--OSI模型 二、网络编程--TCP/IP模型 三、网络接口 四、UDP网络相关编程及主要函数 编辑编辑 UDP的特征 socke函数 bind函数 recvfrom函数(接收函数) sendto函数(发送函数) 五、网络编程之 UDP 用…...
微服务通信安全:深入解析mTLS的原理与实践
🔥「炎码工坊」技术弹药已装填! 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、引言:微服务时代的通信安全挑战 随着云原生和微服务架构的普及,服务间的通信安全成为系统设计的核心议题。传统的单体架构中&…...

高考志愿填报管理系统---开发介绍
高考志愿填报管理系统是一款专为教育机构、学校和教师设计的学生信息管理和志愿填报辅助平台。系统基于Django框架开发,采用现代化的Web技术,为教育工作者提供高效、安全、便捷的学生管理解决方案。 ## 📋 系统概述 ### 🎯 系统定…...