技术标签: spring boot quartz SpringBoot
本文讲述在基于SpringBoot框架的项目中,如何一步一步的集成Quartz框架,项目使用的是PostgreSQL数据库。
<!-- quartz -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
在resource目录下创建servicex-quartz.properties文件:
#============================================================================
# 1. 基本配置
#============================================================================
# 调度标识名, 集群中每一个实例都必须使用相同的名称
org.quartz.scheduler.instanceName = SERVICEX-SCHEDULER-INSTANCE-NAME
# ID设置为自动获取, 每一个实例不能相同
org.quartz.scheduler.instanceId = AUTO
#============================================================================
# 2. 调度器线程池配置
#============================================================================
# 线程池的实现类, 一般使用SimpleThreadPool即可满足需求
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
# 指定线程数无默认值, 至少为1
org.quartz.threadPool.threadCount = 10
# 设置线程的优先级(最大为java.lang.Thread.MAX_PRIORITY 10,最小为Thread.MIN_PRIORITY 1,默认为5)
org.quartz.threadPool.threadPriority = 5
#============================================================================
# 3. 作业存储配置
#============================================================================
# 数据保存方式为数据库持久化
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
# 数据库驱动
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
# 是否加入集群
org.quartz.jobStore.isClustered = true
# 检查集群节点状态的频率, 默认值是 15000(即15秒)
org.quartz.jobStore.clusterCheckinInterval = 15000
org.quartz.jobStore.maxMisfiresToHandleAtATime = 1
org.quartz.jobStore.txIsolationLevelSerializable = true
# 设置调度引擎对触发器超时的忍耐时间 (单位毫秒)
org.quartz.jobStore.misfireThreshold = 12000
# 表的前缀,默认QRTZ_
org.quartz.jobStore.tablePrefix = QRTZ_
在官网中找到对于的SQL脚本:
-- Thanks to Patrick Lightbody for submitting this...
--
-- In your Quartz properties file, you'll need to set
-- org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS;
DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE;
DROP TABLE IF EXISTS QRTZ_LOCKS;
DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_SIMPROP_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_JOB_DETAILS;
DROP TABLE IF EXISTS QRTZ_CALENDARS;
CREATE TABLE QRTZ_JOB_DETAILS
(
SCHED_NAME VARCHAR(120) NOT NULL,
JOB_NAME VARCHAR(200) NOT NULL,
JOB_GROUP VARCHAR(200) NOT NULL,
DESCRIPTION VARCHAR(250) NULL,
JOB_CLASS_NAME VARCHAR(250) NOT NULL,
IS_DURABLE BOOL NOT NULL,
IS_NONCONCURRENT BOOL NOT NULL,
IS_UPDATE_DATA BOOL NOT NULL,
REQUESTS_RECOVERY BOOL NOT NULL,
JOB_DATA BYTEA NULL,
PRIMARY KEY (SCHED_NAME, JOB_NAME, JOB_GROUP)
);
CREATE TABLE QRTZ_TRIGGERS
(
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
JOB_NAME VARCHAR(200) NOT NULL,
JOB_GROUP VARCHAR(200) NOT NULL,
DESCRIPTION VARCHAR(250) NULL,
NEXT_FIRE_TIME BIGINT NULL,
PREV_FIRE_TIME BIGINT NULL,
PRIORITY INTEGER NULL,
TRIGGER_STATE VARCHAR(16) NOT NULL,
TRIGGER_TYPE VARCHAR(8) NOT NULL,
START_TIME BIGINT NOT NULL,
END_TIME BIGINT NULL,
CALENDAR_NAME VARCHAR(200) NULL,
MISFIRE_INSTR SMALLINT NULL,
JOB_DATA BYTEA NULL,
PRIMARY KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME, JOB_NAME, JOB_GROUP)
REFERENCES QRTZ_JOB_DETAILS (SCHED_NAME, JOB_NAME, JOB_GROUP)
);
CREATE TABLE QRTZ_SIMPLE_TRIGGERS
(
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
REPEAT_COUNT BIGINT NOT NULL,
REPEAT_INTERVAL BIGINT NOT NULL,
TIMES_TRIGGERED BIGINT NOT NULL,
PRIMARY KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP)
);
CREATE TABLE QRTZ_CRON_TRIGGERS
(
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
CRON_EXPRESSION VARCHAR(120) NOT NULL,
TIME_ZONE_ID VARCHAR(80),
PRIMARY KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP)
);
CREATE TABLE QRTZ_SIMPROP_TRIGGERS
(
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
STR_PROP_1 VARCHAR(512) NULL,
STR_PROP_2 VARCHAR(512) NULL,
STR_PROP_3 VARCHAR(512) NULL,
INT_PROP_1 INT NULL,
INT_PROP_2 INT NULL,
LONG_PROP_1 BIGINT NULL,
LONG_PROP_2 BIGINT NULL,
DEC_PROP_1 NUMERIC(13, 4) NULL,
DEC_PROP_2 NUMERIC(13, 4) NULL,
BOOL_PROP_1 BOOL NULL,
BOOL_PROP_2 BOOL NULL,
PRIMARY KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP)
);
CREATE TABLE QRTZ_BLOB_TRIGGERS
(
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
BLOB_DATA BYTEA NULL,
PRIMARY KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP)
);
CREATE TABLE QRTZ_CALENDARS
(
SCHED_NAME VARCHAR(120) NOT NULL,
CALENDAR_NAME VARCHAR(200) NOT NULL,
CALENDAR BYTEA NOT NULL,
PRIMARY KEY (SCHED_NAME, CALENDAR_NAME)
);
CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS
(
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
PRIMARY KEY (SCHED_NAME, TRIGGER_GROUP)
);
CREATE TABLE QRTZ_FIRED_TRIGGERS
(
SCHED_NAME VARCHAR(120) NOT NULL,
ENTRY_ID VARCHAR(95) NOT NULL,
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
INSTANCE_NAME VARCHAR(200) NOT NULL,
FIRED_TIME BIGINT NOT NULL,
SCHED_TIME BIGINT NOT NULL,
PRIORITY INTEGER NOT NULL,
STATE VARCHAR(16) NOT NULL,
JOB_NAME VARCHAR(200) NULL,
JOB_GROUP VARCHAR(200) NULL,
IS_NONCONCURRENT BOOL NULL,
REQUESTS_RECOVERY BOOL NULL,
PRIMARY KEY (SCHED_NAME, ENTRY_ID)
);
CREATE TABLE QRTZ_SCHEDULER_STATE
(
SCHED_NAME VARCHAR(120) NOT NULL,
INSTANCE_NAME VARCHAR(200) NOT NULL,
LAST_CHECKIN_TIME BIGINT NOT NULL,
CHECKIN_INTERVAL BIGINT NOT NULL,
PRIMARY KEY (SCHED_NAME, INSTANCE_NAME)
);
CREATE TABLE QRTZ_LOCKS
(
SCHED_NAME VARCHAR(120) NOT NULL,
LOCK_NAME VARCHAR(40) NOT NULL,
PRIMARY KEY (SCHED_NAME, LOCK_NAME)
);
CREATE INDEX IDX_QRTZ_J_REQ_RECOVERY
ON QRTZ_JOB_DETAILS (SCHED_NAME, REQUESTS_RECOVERY);
CREATE INDEX IDX_QRTZ_J_GRP
ON QRTZ_JOB_DETAILS (SCHED_NAME, JOB_GROUP);
CREATE INDEX IDX_QRTZ_T_J
ON QRTZ_TRIGGERS (SCHED_NAME, JOB_NAME, JOB_GROUP);
CREATE INDEX IDX_QRTZ_T_JG
ON QRTZ_TRIGGERS (SCHED_NAME, JOB_GROUP);
CREATE INDEX IDX_QRTZ_T_C
ON QRTZ_TRIGGERS (SCHED_NAME, CALENDAR_NAME);
CREATE INDEX IDX_QRTZ_T_G
ON QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_GROUP);
CREATE INDEX IDX_QRTZ_T_STATE
ON QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_N_STATE
ON QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP, TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_N_G_STATE
ON QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_GROUP, TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_NEXT_FIRE_TIME
ON QRTZ_TRIGGERS (SCHED_NAME, NEXT_FIRE_TIME);
CREATE INDEX IDX_QRTZ_T_NFT_ST
ON QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_STATE, NEXT_FIRE_TIME);
CREATE INDEX IDX_QRTZ_T_NFT_MISFIRE
ON QRTZ_TRIGGERS (SCHED_NAME, MISFIRE_INSTR, NEXT_FIRE_TIME);
CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE
ON QRTZ_TRIGGERS (SCHED_NAME, MISFIRE_INSTR, NEXT_FIRE_TIME, TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE_GRP
ON QRTZ_TRIGGERS (SCHED_NAME, MISFIRE_INSTR, NEXT_FIRE_TIME, TRIGGER_GROUP, TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_FT_TRIG_INST_NAME
ON QRTZ_FIRED_TRIGGERS (SCHED_NAME, INSTANCE_NAME);
CREATE INDEX IDX_QRTZ_FT_INST_JOB_REQ_RCVRY
ON QRTZ_FIRED_TRIGGERS (SCHED_NAME, INSTANCE_NAME, REQUESTS_RECOVERY);
CREATE INDEX IDX_QRTZ_FT_J_G
ON QRTZ_FIRED_TRIGGERS (SCHED_NAME, JOB_NAME, JOB_GROUP);
CREATE INDEX IDX_QRTZ_FT_JG
ON QRTZ_FIRED_TRIGGERS (SCHED_NAME, JOB_GROUP);
CREATE INDEX IDX_QRTZ_FT_T_G
ON QRTZ_FIRED_TRIGGERS (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP);
CREATE INDEX IDX_QRTZ_FT_TG
ON QRTZ_FIRED_TRIGGERS (SCHED_NAME, TRIGGER_GROUP);
COMMIT;
@Configuration
public class SchedulerConfig {
// 配置文件路径
private static final String SERVICEX_QUARTZ_CONFIG_PATH = "/servicex-quartz.properties";
// 控制器工厂类
@Bean
public SchedulerFactoryBean schedulerFactoryBean(DataSource dataSource) {
SchedulerFactoryBean factory = new SchedulerFactoryBean();
factory.setDataSource(dataSource);
factory.setQuartzProperties(quartzProperties());
factory.setStartupDelay(1);
factory.setApplicationContextSchedulerContextKey("SERVICEX-APPLICATION-CONTEXT-KEY");
factory.setOverwriteExistingJobs(true);
factory.setAutoStartup(true);
return factory;
}
@Bean
@ConditionalOnResource(resources = SERVICEX_QUARTZ_CONFIG_PATH)
Properties quartzProperties() {
try {
PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
propertiesFactoryBean.setLocation(new ClassPathResource(SERVICEX_QUARTZ_CONFIG_PATH));
propertiesFactoryBean.afterPropertiesSet();
Properties properties = propertiesFactoryBean.getObject();
return properties;
} catch (IOException e) {
System.out.println("QUARTZ相关的配置文件不存在.");
e.printStackTrace();
}
return null;
}
}
/**
* 抽象JOB的封装
* JOB表示一个任务, 也就是执行的具体的内容, 一个JOB可以被多个TRIGGER关联, 但是一个TRIGGER只能关联一个JOB.
* @author ROCKY
* @createTime 2022年07月04日
*/
@Slf4j
public abstract class AbstractQuartzJob implements Job {
// 线程本地变量
private static ThreadLocal<Date> threadLocal = new ThreadLocal<>();
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
ServicexJob job = new ServicexJob();
BeanUtils.copyProperties(context.getMergedJobDataMap().get(ScheduleConstants.TASK_PROPERTIES), job);
try {
before(context, job);
if (job != null) {
doExecute(context, job);
}
after(context, job, null);
} catch (Exception e) {
after(context, job, e);
log.error("任务执行异常:", e);
}
}
// 1. 执行前
protected void before(JobExecutionContext context, ServicexJob job) {
threadLocal.set(new Date());
}
// 2. 执行方法, 实际的子JOB重载该方法
protected abstract void doExecute(JobExecutionContext context, ServicexJob job) throws Exception;
// 3. 执行后
protected void after(JobExecutionContext context, ServicexJob job, Exception e) {
threadLocal.remove();
// 质检完成后会做一些操作
job.doSomeThings();
}
}
/**
* 不支持并发执行任务的JOB(禁止并发执行, 表示Quartz不能并发的执行同一个JOB的定义, 特指一个JOB类的多个实例)
* @author ROCKY
* @createTime 2022年07月04日
*/
@Slf4j
@DisallowConcurrentExecution
public class MyJobExecution extends AbstractQuartzJob {
@Override
protected void doExecute(JobExecutionContext context, ServicexJob job) throws Exception {
// 真正的调用逻辑
log.info("TO DO SOMETHING."+ job.getCronExpression() + "[" + job.getNextValidTime() + "]");
}
}
/**
* 调度器服务
* JobDetail:用于定义作业的实例
* Trigger: 触发器, 执行给定作业计划的组件实例
* Scheduler: 与调度程序交互的主要的API
* CronScheduleBuilder:用于创建一个Scheduler的生成器
* 何时触发, 通过Trigger来定义, 使用TriggerBuilder进行构建
* 什么任务, 通过JobDetail来定义, 使用JobBuilder进行构建
* 执行逻辑, 通过JOB中的doExecute方法的具体实现, 执行具体的内容
*
* @author ROCKY
* @createTime 2022年07月04日
*/
public class ServicexScheduler {
// 1. 创建/新增任务
public static void register(Scheduler scheduler, ServicexJob job, Class<? extends Job> jobClass) throws SchedulerException {
// 1. 构建任务信息
String jobId = job.getJobId();
String jobGroup = job.getJobGroup();
// 2.1 构建JOB-DETAIL
JobKey jobKey = getJobKey(jobId, jobGroup);
TriggerKey triggerKey = getTriggerKey(jobId, jobGroup);
JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobKey).build();
// 2.2 设置参数
jobDetail.getJobDataMap().put(ScheduleConstants.TASK_PROPERTIES, job);
// 3. 构建CRON调度器
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression());
try {
cronScheduleBuilder = handleCronScheduleMisfirePolicy(job, cronScheduleBuilder);
} catch (Exception e) {
e.printStackTrace();
}
// 4. 构建触发器
// .startAt(), 用于指定开始时间
CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(triggerKey)
.startAt(job.getStartTime())
.withSchedule(cronScheduleBuilder).build();
// 5. 判断是否存在该定时任务
if (scheduler.checkExists(jobKey)) {
// 防止创建时存在数据问题(先移除再创建)
scheduler.deleteJob(jobKey);
}
// 6. 根据JOB-DETAIL和建触发器创建定时任务
scheduler.scheduleJob(jobDetail, trigger);
// 7. 如果JOB的状态是暂停的/禁用的, 需要暂定该任务
if (!job.getStatus().equals(ScheduleConstants.Status.ENABLED.getValue())) {
scheduler.pauseJob(jobKey);
}
}
public static void register(Scheduler scheduler, ServicexJob job) throws SchedulerException {
register(scheduler, job, MyJobExecution.class);
}
// 2. 暂停任务
public static int pause(Scheduler scheduler, ServicexJob job) throws SchedulerException {
String jobId = job.getJobId();
String jobGroup = job.getJobGroup();
JobKey jobKey = getJobKey(jobId, jobGroup);
if (StringUtils.isNotEmpty(jobId) && StringUtils.isNotEmpty(jobGroup) && scheduler.checkExists(jobKey)) {
scheduler.pauseJob(jobKey);
return 1;
}
return -1;
}
// 3. 恢复任务
public static int resume(Scheduler scheduler, ServicexJob job) throws SchedulerException {
String jobId = job.getJobId();
String jobGroup = job.getJobGroup();
JobKey jobKey = getJobKey(jobId, jobGroup);
if (StringUtils.isNotEmpty(jobId) && StringUtils.isNotEmpty(jobGroup) && scheduler.checkExists(jobKey)) {
scheduler.resumeJob(jobKey);
return 1;
}
return -1;
}
// 4. 删除任务
public static int delete(Scheduler scheduler, ServicexJob job) throws SchedulerException {
String jobId = job.getJobId();
String jobGroup = job.getJobGroup();
JobKey jobKey = getJobKey(jobId, jobGroup);
if (StringUtils.isNotEmpty(jobId) && StringUtils.isNotEmpty(jobGroup) && scheduler.checkExists(jobKey)) {
scheduler.deleteJob(jobKey);
return 1;
}
return -1;
}
// 5. 运行任务
public static void run(Scheduler scheduler, ServicexJob job) throws SchedulerException {
String jobId = job.getJobId();
String jobGroup = job.getJobGroup();
JobDataMap dataMap = new JobDataMap();
dataMap.put(ScheduleConstants.TASK_PROPERTIES, job);
scheduler.triggerJob(getJobKey(jobId, jobGroup), dataMap);
}
// 6. 更新任务
public static void update(Scheduler scheduler, ServicexJob job) throws SchedulerException {
String jobId = job.getJobId();
String jobGroup = job.getJobGroup();
JobKey jobKey = getJobKey(jobId, jobGroup);
if (StringUtils.isNotEmpty(jobId) && StringUtils.isNotEmpty(jobGroup) && scheduler.checkExists(jobKey)) {
// 移除
scheduler.deleteJob(jobKey);
}
// 新建
register(scheduler, job);
}
// 6. 更新任务
public static void update(Scheduler scheduler, ServicexJob job, Class<? extends Job> jobClass) throws SchedulerException {
String jobId = job.getJobId();
String jobGroup = job.getJobGroup();
JobKey jobKey = getJobKey(jobId, jobGroup);
if (StringUtils.isNotEmpty(jobId) && StringUtils.isNotEmpty(jobGroup) && scheduler.checkExists(jobKey)) {
// 移除
scheduler.deleteJob(jobKey);
}
// 新建
register(scheduler, job, jobClass);
}
// 获取任务的键
private static JobKey getJobKey(String jobId, String jobGroup) {
return JobKey.jobKey(ScheduleConstants.TASK_CLASS_NAME_PREFIX + jobId, jobGroup);
}
// 获取任务的键
private static JobKey getJobKey(String taskName, String jobId, String jobGroup) {
String jobName = (StringUtils.isNotEmpty(taskName) ? taskName : ScheduleConstants.TASK_CLASS_NAME_PREFIX) + jobId;
return JobKey.jobKey(jobName, jobGroup);
}
// 构建任务触发器
private static TriggerKey getTriggerKey(String jobId, String jobGroup) {
return TriggerKey.triggerKey(ScheduleConstants.TASK_CLASS_NAME_PREFIX + jobId, jobGroup);
}
// 设置定时任务执行计划的失火策略
private static CronScheduleBuilder handleCronScheduleMisfirePolicy(ServicexJob job, CronScheduleBuilder cb)
throws Exception {
switch (job.getMisfirePolicy()) {
case ScheduleConstants.MISFIRE_DEFAULT:
return cb;
case ScheduleConstants.MISFIRE_IGNORE_MISFIRES:
return cb.withMisfireHandlingInstructionIgnoreMisfires();
case ScheduleConstants.MISFIRE_FIRE_AND_PROCEED:
return cb.withMisfireHandlingInstructionFireAndProceed();
case ScheduleConstants.MISFIRE_DO_NOTHING:
return cb.withMisfireHandlingInstructionDoNothing();
default:
return null;
}
}
}
在服务启动时注册所有的任务:
@Service
@Slf4j
public class ServicexQuartzServiceImpl implements IServicexQuartzService {
@Autowired
private ServicexQuartzMapperservicexQuartzMapper;
@Autowired
private Scheduler scheduler;
@PostConstruct
public void init() throws SchedulerException {
scheduler.clear();
List<ServicexJob> jobs = new LambdaQueryChainWrapper<>(servicexQuartzMapper).eq(ServicexJob::getRunType, RunType.AUTO.getValue()).list();
for (ServicexJob job : jobs) {
ServicexScheduler.register(scheduler, job);
}
}
...
}
SpringBoot - 集成Quartz框架之CRON表达式
SpringBoot - 集成Quartz框架之Quartz简介(一)
SpringBoot - 集成Quartz框架之常用配置(二)
SpringBoot - 集成Quartz框架之具体步骤(三)
SpringBoot - 集成Quartz框架之独立数据源(四)
SpringBoot - 集成Quartz框架之常见问题(五)
SpringBoot - 集成Quartz框架之@DisallowConcurrentExecution注解详解(六)
文章浏览阅读645次。这个肯定是末尾的IDAT了,因为IDAT必须要满了才会开始一下个IDAT,这个明显就是末尾的IDAT了。,对应下面的create_head()代码。,对应下面的create_tail()代码。不要考虑爆破,我已经试了一下,太多情况了。题目来源:UNCTF。_攻防世界困难模式攻略图文
文章浏览阅读2.9k次,点赞3次,收藏10次。偶尔会用到,记录、分享。1. 数据库导出1.1 切换到dmdba用户su - dmdba1.2 进入达梦数据库安装路径的bin目录,执行导库操作 导出语句:./dexp cwy_init/[email protected]:5236 file=cwy_init.dmp log=cwy_init_exp.log 注释: cwy_init/init_123..._达梦数据库导入导出
文章浏览阅读1.9k次。1. 在官网上下载KindEditor文件,可以删掉不需要要到的jsp,asp,asp.net和php文件夹。接着把文件夹放到项目文件目录下。2. 修改html文件,在页面引入js文件:<script type="text/javascript" src="./kindeditor/kindeditor-all.js"></script><script type="text/javascript" src="./kindeditor/lang/zh-CN.js"_kindeditor.js
文章浏览阅读2.3k次,点赞6次,收藏14次。SPI的详情简介不必赘述。假设我们通过SPI发送0xAA,我们的数据线就会变为10101010,通过修改不同的内容,即可修改SPI中0和1的持续时间。比如0xF0即为前半周期为高电平,后半周期为低电平的状态。在SPI的通信模式中,CPHA配置会影响该实验,下图展示了不同采样位置的SPI时序图[1]。CPOL = 0,CPHA = 1:CLK空闲状态 = 低电平,数据在下降沿采样,并在上升沿移出CPOL = 0,CPHA = 0:CLK空闲状态 = 低电平,数据在上升沿采样,并在下降沿移出。_stm32g431cbu6
文章浏览阅读1.2k次,点赞2次,收藏8次。数据链路层习题自测问题1.数据链路(即逻辑链路)与链路(即物理链路)有何区别?“电路接通了”与”数据链路接通了”的区别何在?2.数据链路层中的链路控制包括哪些功能?试讨论数据链路层做成可靠的链路层有哪些优点和缺点。3.网络适配器的作用是什么?网络适配器工作在哪一层?4.数据链路层的三个基本问题(帧定界、透明传输和差错检测)为什么都必须加以解决?5.如果在数据链路层不进行帧定界,会发生什么问题?6.PPP协议的主要特点是什么?为什么PPP不使用帧的编号?PPP适用于什么情况?为什么PPP协议不_接收方收到链路层数据后,使用crc检验后,余数为0,说明链路层的传输时可靠传输
文章浏览阅读587次。软件测试工程师移民加拿大 无证移民,未受过软件工程师的教育(第1部分) (Undocumented Immigrant With No Education to Software Engineer(Part 1))Before I start, I want you to please bear with me on the way I write, I have very little gen...
文章浏览阅读304次。Thinkpad X250笔记本电脑,装的是FreeBSD,进入BIOS修改虚拟化配置(其后可能是误设置了安全开机),保存退出后系统无法启动,显示:secure boot failed ,把自己惊出一身冷汗,因为这台笔记本刚好还没开始做备份.....根据错误提示,到bios里面去找相关配置,在Security里面找到了Secure Boot选项,发现果然被设置为Enabled,将其修改为Disabled ,再开机,终于正常启动了。_安装完系统提示secureboot failure
文章浏览阅读10w+次,点赞93次,收藏352次。1、用strtok函数进行字符串分割原型: char *strtok(char *str, const char *delim);功能:分解字符串为一组字符串。参数说明:str为要分解的字符串,delim为分隔符字符串。返回值:从str开头开始的一个个被分割的串。当没有被分割的串时则返回NULL。其它:strtok函数线程不安全,可以使用strtok_r替代。示例://借助strtok实现split#include <string.h>#include <stdio.h&_c++ 字符串分割
文章浏览阅读2.3k次。1 .高斯日记 大数学家高斯有个好习惯:无论如何都要记日记。他的日记有个与众不同的地方,他从不注明年月日,而是用一个整数代替,比如:4210后来人们知道,那个整数就是日期,它表示那一天是高斯出生后的第几天。这或许也是个好习惯,它时时刻刻提醒着主人:日子又过去一天,还有多少时光可以用于浪费呢?高斯出生于:1777年4月30日。在高斯发现的一个重要定理的日记_2013年第四届c a组蓝桥杯省赛真题解答
文章浏览阅读851次,点赞17次,收藏22次。摘要:本文利用供需算法对核极限学习机(KELM)进行优化,并用于分类。
文章浏览阅读1.1k次。一、系统弱密码登录1、在kali上执行命令行telnet 192.168.26.1292、Login和password都输入msfadmin3、登录成功,进入系统4、测试如下:二、MySQL弱密码登录:1、在kali上执行mysql –h 192.168.26.129 –u root2、登录成功,进入MySQL系统3、测试效果:三、PostgreSQL弱密码登录1、在Kali上执行psql -h 192.168.26.129 –U post..._metasploitable2怎么进入
文章浏览阅读257次。本文将为初学者提供Python学习的详细指南,从Python的历史、基础语法和数据类型到面向对象编程、模块和库的使用。通过本文,您将能够掌握Python编程的核心概念,为今后的编程学习和实践打下坚实基础。_python人工智能开发从入门到精通pdf