【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接口等。 以上图页面为例,一个基础数据信息页面可以分为,分类ÿ…...
应用升级/灾备测试时使用guarantee 闪回点迅速回退
1.场景 应用要升级,当升级失败时,数据库回退到升级前. 要测试系统,测试完成后,数据库要回退到测试前。 相对于RMAN恢复需要很长时间, 数据库闪回只需要几分钟。 2.技术实现 数据库设置 2个db_recovery参数 创建guarantee闪回点,不需要开启数据库闪回。…...
label-studio的使用教程(导入本地路径)
文章目录 1. 准备环境2. 脚本启动2.1 Windows2.2 Linux 3. 安装label-studio机器学习后端3.1 pip安装(推荐)3.2 GitHub仓库安装 4. 后端配置4.1 yolo环境4.2 引入后端模型4.3 修改脚本4.4 启动后端 5. 标注工程5.1 创建工程5.2 配置图片路径5.3 配置工程类型标签5.4 配置模型5.…...
k8s从入门到放弃之Ingress七层负载
k8s从入门到放弃之Ingress七层负载 在Kubernetes(简称K8s)中,Ingress是一个API对象,它允许你定义如何从集群外部访问集群内部的服务。Ingress可以提供负载均衡、SSL终结和基于名称的虚拟主机等功能。通过Ingress,你可…...
IGP(Interior Gateway Protocol,内部网关协议)
IGP(Interior Gateway Protocol,内部网关协议) 是一种用于在一个自治系统(AS)内部传递路由信息的路由协议,主要用于在一个组织或机构的内部网络中决定数据包的最佳路径。与用于自治系统之间通信的 EGP&…...
SCAU期末笔记 - 数据分析与数据挖掘题库解析
这门怎么题库答案不全啊日 来简单学一下子来 一、选择题(可多选) 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘:专注于发现数据中…...
DAY 47
三、通道注意力 3.1 通道注意力的定义 # 新增:通道注意力模块(SE模块) class ChannelAttention(nn.Module):"""通道注意力模块(Squeeze-and-Excitation)"""def __init__(self, in_channels, reduction_rat…...
《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》
在注意力分散、内容高度同质化的时代,情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现,消费者对内容的“有感”程度,正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中࿰…...
微信小程序 - 手机震动
一、界面 <button type"primary" bindtap"shortVibrate">短震动</button> <button type"primary" bindtap"longVibrate">长震动</button> 二、js逻辑代码 注:文档 https://developers.weixin.qq…...
TRS收益互换:跨境资本流动的金融创新工具与系统化解决方案
一、TRS收益互换的本质与业务逻辑 (一)概念解析 TRS(Total Return Swap)收益互换是一种金融衍生工具,指交易双方约定在未来一定期限内,基于特定资产或指数的表现进行现金流交换的协议。其核心特征包括&am…...
Element Plus 表单(el-form)中关于正整数输入的校验规则
目录 1 单个正整数输入1.1 模板1.2 校验规则 2 两个正整数输入(联动)2.1 模板2.2 校验规则2.3 CSS 1 单个正整数输入 1.1 模板 <el-formref"formRef":model"formData":rules"formRules"label-width"150px"…...
