SpringBoot整合 Quartz實現(xiàn)定時推送實戰(zhàn)指南
前言
根據(jù)需求(多條不確定的結(jié)束時間 + 提前 N 分鐘推送),Spring Boot 中最優(yōu)方案是結(jié)合 Quartz 動態(tài)定時任務(wù) + 任務(wù)持久化,支持動態(tài)添加 / 刪除結(jié)束時間、自動計算提前 N 分鐘的觸發(fā)點,且能應(yīng)對服務(wù)重啟后任務(wù)不丟失的問題。
一、Quartz 是什么?

Quartz 是一個功能強大、開源的任務(wù)調(diào)度框架,用于實現(xiàn)定時、周期性或基于特定規(guī)則的任務(wù)執(zhí)行。簡單來說,它就是 Java 生態(tài)中 “定時任務(wù)” 的標準解決方案,支持復(fù)雜的調(diào)度邏輯,廣泛應(yīng)用于后臺系統(tǒng)的定時任務(wù)場景(如定時備份、數(shù)據(jù)同步、定時推送、報表生成等)。
1、核心定位:解決什么問題?
日常開發(fā)中,我們常需要 “在特定時間執(zhí)行某段代碼”,比如:
- 每天凌晨 2 點執(zhí)行數(shù)據(jù)庫備份;
- 每隔 30 分鐘同步一次第三方數(shù)據(jù);
- 每月 1 號生成上月報表;
- 某個固定時間點觸發(fā)短信推送。
Java 原生提供了 Timer/TimerTask,但存在明顯缺陷(如單線程執(zhí)行、不支持復(fù)雜表達式、異常會導(dǎo)致線程終止),無法滿足企業(yè)級需求。而 Quartz 彌補了這些不足,提供了:
- 復(fù)雜調(diào)度規(guī)則(支持 Cron 表達式,覆蓋幾乎所有時間場景);
- 任務(wù)與調(diào)度解耦(任務(wù)邏輯和執(zhí)行規(guī)則分離);
- 高可用(支持集群部署,避免單點故障);
- 可持久化(任務(wù)和調(diào)度狀態(tài)可存儲到數(shù)據(jù)庫,重啟后不丟失);
- 并發(fā)控制(支持任務(wù)并發(fā)執(zhí)行或串行執(zhí)行)。
2、Quartz 核心組件
Quartz 的架構(gòu)設(shè)計清晰,核心組件分為 3 類,需理解它們的職責(zé)和關(guān)系:
| 組件 | 作用 |
|---|---|
| Job(任務(wù)) | 具體要執(zhí)行的 “業(yè)務(wù)邏輯”,需實現(xiàn) org.quartz.Job 接口(重寫 execute() 方法)。 |
| JobDetail(任務(wù)詳情) | 描述 Job 的元數(shù)據(jù)(如任務(wù)名稱、分組、是否持久化等),是 Quartz 內(nèi)部管理 Job 的載體(Job 本身是業(yè)務(wù)邏輯,JobDetail 是管理配置)。 |
| Trigger(觸發(fā)器) | 定義 Job 的 “執(zhí)行規(guī)則”(何時執(zhí)行、執(zhí)行頻率),是觸發(fā) Job 執(zhí)行的 “開關(guān)”。常見實現(xiàn):SimpleTrigger:簡單規(guī)則(如延遲 5 秒執(zhí)行、每隔 10 秒執(zhí)行 3 次);CronTrigger:復(fù)雜規(guī)則(基于 Cron 表達式,如每天 02:00 執(zhí)行)。 |
| Scheduler(調(diào)度器) | Quartz 的核心 “大腦”,負責(zé)管理 JobDetail 和 Trigger,根據(jù) Trigger 規(guī)則觸發(fā) Job 執(zhí)行。需通過 SchedulerFactory 創(chuàng)建,是任務(wù)調(diào)度的入口。 |
二、使用步驟
1. 引入依賴(Spring Boot + Quartz + 數(shù)據(jù)庫)
需要 Quartz 核心依賴、Spring 整合包,以及數(shù)據(jù)庫依賴(用于任務(wù)持久化):
<!-- Spring Boot 整合 Quartz -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
<!-- 數(shù)據(jù)庫依賴(以 MySQL 為例,用于任務(wù)持久化) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>2. 配置文件(application.yml)
配置數(shù)據(jù)庫連接和 Quartz 持久化(關(guān)鍵:禁用內(nèi)存存儲,啟用數(shù)據(jù)庫存儲):
yaml
spring:
# 數(shù)據(jù)庫配置(Quartz 任務(wù)將存儲到該庫)
datasource:
url: jdbc:mysql://localhost:3306/quartz_db?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
# JPA 配置(可選,用于存儲業(yè)務(wù)結(jié)束時間,方便管理)
jpa:
hibernate:
ddl-auto: update
show-sql: true
# Quartz 配置(核心:數(shù)據(jù)庫持久化)
quartz:
job-store-type: jdbc # 任務(wù)存儲類型:jdbc(數(shù)據(jù)庫),默認是 memory(內(nèi)存)
jdbc:
initialize-schema: always # 自動創(chuàng)建 Quartz 所需表(首次啟動用 always,后續(xù)改為 never)
properties:
org:
quartz:
scheduler:
instanceName: pushScheduler
instanceId: AUTO # 集群模式下自動分配實例ID
jobStore:
class: org.quartz.impl.jdbcjobstore.JobStoreTX
driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
tablePrefix: QRTZ_ # Quartz 表前綴(自動創(chuàng)建的表會帶此前綴)
isClustered: false # 單節(jié)點用 false,集群部署改為 true
clusterCheckinInterval: 20000
threadPool:
class: org.quartz.simpl.SimpleThreadPool
threadCount: 10 # 線程池大小3. 定義業(yè)務(wù)實體(存儲結(jié)束時間,可選但推薦)
用于記錄用戶添加的 “結(jié)束時間”,方便后續(xù)查詢、修改、刪除任務(wù)(關(guān)聯(lián) Quartz 任務(wù) ID):
import jakarta.persistence.*;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@Entity
@Table(name = "push_task")
public class PushTask {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id; // 自增ID
@Column(name = "end_time", nullable = false)
private LocalDateTime endTime; // 原始結(jié)束時間(如明天3點)
@Column(name = "trigger_time", nullable = false)
private LocalDateTime triggerTime; // 推送觸發(fā)時間(endTime -5分鐘)
@Column(name = "quartz_job_id", unique = true, nullable = false)
private String quartzJobId; // 關(guān)聯(lián) Quartz 的 JobDetail ID
@Column(name = "task_desc")
private String taskDesc; // 任務(wù)描述(如“訂單123超時提醒”)
@Column(name = "status")
private Integer status; // 狀態(tài):0-未觸發(fā) 1-已觸發(fā) 2-已取消
}4. 實現(xiàn)消息推送 Job(Quartz 任務(wù)邏輯)
Quartz 任務(wù)的核心邏輯:觸發(fā)時執(zhí)行消息推送,同時更新業(yè)務(wù)任務(wù)狀態(tài):
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
/**
* 消息推送 Job(Quartz 任務(wù))
*/
@Component
public class PushMessageJob implements Job {
private static final Logger logger = LoggerFactory.getLogger(PushMessageJob.class);
@Autowired
private PushTaskRepository pushTaskRepository; // 后續(xù)定義的 DAO 接口
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
// 1. 從 Job 上下文獲取參數(shù)( quartzJobId + 任務(wù)描述 )
String quartzJobId = context.getJobDetail().getKey().getName();
String taskDesc = (String) context.getJobDetail().getJobDataMap().get("taskDesc");
String triggerTimeDate = (String) context.getJobDetail().getJobDataMap().get("triggerTime");
LocalDateTime triggerTime = null;
if (null != triggerTimeDate) {
triggerTime = LocalDateTime.parse(triggerTimeDate);
}
try {
// 2. 核心:執(zhí)行消息推送(替換為你的實際邏輯:短信、APP推送、郵件等)
doPush(taskDesc, triggerTime);
// 3. 更新業(yè)務(wù)任務(wù)狀態(tài)為“已觸發(fā)”
pushTaskRepository.updateStatusByQuartzJobId(1, quartzJobId);
logger.info("消息推送成功!quartzJobId:{},任務(wù)描述:{},觸發(fā)時間:{}",
quartzJobId, taskDesc, triggerTime);
} catch (Exception e) {
logger.error("消息推送失??!quartzJobId:{}", quartzJobId, e);
// 可選:拋出異常觸發(fā) Quartz 重試(需配置重試策略)
throw new JobExecutionException("推送失敗,觸發(fā)重試", e, true);
}
}
/**
* 實際推送邏輯(根據(jù)業(yè)務(wù)需求替換)
*/
private void doPush(String taskDesc, LocalDateTime triggerTime) {
// 示例1:打印日志(實際場景替換為第三方推送接口)
System.out.printf("[推送通知] 任務(wù)描述:%s,觸發(fā)時間:%s,內(nèi)容:即將到達結(jié)束時間(提前5分鐘提醒)%n",
taskDesc, triggerTime);
// 示例2:調(diào)用 HTTP 推送接口(如極光推送)
// RestTemplate restTemplate = new RestTemplate();
// String pushUrl = "https://api.jpush.cn/v3/push";
// PushRequest request = new PushRequest();
// request.setContent("即將到達結(jié)束時間:" + taskDesc);
// restTemplate.postForObject(pushUrl, request, String.class);
}
}5. 定義 DAO 接口(操作業(yè)務(wù)任務(wù)表)
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
public interface PushTaskRepository extends JpaRepository<PushTask, Long> {
// 根據(jù) QuartzJobId 查詢?nèi)蝿?wù)
PushTask findByQuartzJobId(String quartzJobId);
// 更新任務(wù)狀態(tài)
@Modifying
@Transactional
@Query("update PushTask t set t.status = :status where t.quartzJobId = :quartzJobId")
int updateStatusByQuartzJobId(Integer status, String quartzJobId);
// 根據(jù)狀態(tài)和觸發(fā)時間查詢未執(zhí)行的任務(wù)(服務(wù)重啟后恢復(fù)任務(wù)用)
@Query("select t from PushTask t where t.status = 0 and t.triggerTime > :now")
List<PushTask> findUnTriggeredTasks(LocalDateTime now);
}6. 動態(tài)定時任務(wù)服務(wù)(核心:添加 / 刪除 / 恢復(fù)任務(wù))
封裝 Quartz API,實現(xiàn)動態(tài)添加任務(wù)(自動計算提前 5 分鐘)、刪除任務(wù)、服務(wù)重啟后恢復(fù)未觸發(fā)任務(wù):
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.List;
import java.util.UUID;
@Service
public class DynamicQuartzService {
@Autowired
private Scheduler scheduler;
@Autowired
private PushTaskRepository pushTaskRepository;
// 任務(wù)組名(統(tǒng)一分組,方便管理)
private static final String JOB_GROUP = "PUSH_JOB_GROUP";
private static final String TRIGGER_GROUP = "PUSH_TRIGGER_GROUP";
/**
* 核心方法:添加推送任務(wù)(自動計算提前5分鐘觸發(fā))
*
* @params endTime 原始結(jié)束時間(如明天3點)
* @params triggerTime 觸發(fā)時間(如明天2點)
* @params taskDesc 任務(wù)描述(如“訂單123超時提醒”)
* @return 保存的業(yè)務(wù)任務(wù)
*/
public PushTask addPushTask(LocalDateTime endTime, LocalDateTime triggerTime,String taskDesc) throws SchedulerException {
PaAssert.notNull(endTime, "結(jié)束時間不能為空!");
PaAssert.notNull(triggerTime, "觸發(fā)時間不能為空!");
// 校驗結(jié)束時間
LocalDateTime now = LocalDateTime.now();
if (endTime.isBefore(now) || endTime.isEqual(now)) {
throw new IllegalArgumentException("結(jié)束時間必須晚于當前時間!");
}
if (triggerTime.isBefore(now) || triggerTime.isEqual(now)) {
throw new IllegalArgumentException("觸發(fā)時間必須晚于當前時間!");
}
// 3. 生成唯一的 QuartzJobId(避免重復(fù))
String quartzJobId = "PUSH_JOB_" + UUID.randomUUID().toString().replace("-", "");
// 4. 構(gòu)建 Quartz JobDetail(綁定 PushMessageJob)
JobDetail jobDetail = JobBuilder.newJob(PushMessageJob.class)
.withIdentity(quartzJobId, JOB_GROUP) // 任務(wù)ID:quartzJobId,組名:JOB_GROUP
.usingJobData("taskDesc", taskDesc)
.usingJobData("triggerTime", String.valueOf(triggerTime))
.storeDurably() // 持久化(無觸發(fā)器時也保留)
.build();
// 5. 構(gòu)建 CronTrigger(根據(jù) triggerTime 生成 Cron 表達式)
String cronExpression = buildCronExpression(triggerTime);
CronTrigger trigger = TriggerBuilder.newTrigger()
.withIdentity(quartzJobId, TRIGGER_GROUP)
.forJob(jobDetail)
.withSchedule(CronScheduleBuilder.cronSchedule(cronExpression)
.withMisfireHandlingInstructionFireAndProceed()) // 錯過觸發(fā)時立即執(zhí)行
.startNow()
// 關(guān)鍵:觸發(fā)時間+1分鐘,確保僅執(zhí)行一次(避免循環(huán)或重復(fù)觸發(fā))
.endAt(Date.from(triggerTime.plusMinutes(1).atZone(ZoneId.systemDefault()).toInstant()))
.build();
// 6. 注冊任務(wù)到 Quartz 調(diào)度器
scheduler.scheduleJob(jobDetail, trigger);
// 7. 保存業(yè)務(wù)任務(wù)到數(shù)據(jù)庫(關(guān)聯(lián) QuartzJobId)
PushTask pushTask = new PushTask();
pushTask.setEndTime(endTime);
pushTask.setTriggerTime(triggerTime);
pushTask.setQuartzJobId(quartzJobId);
pushTask.setTaskDesc(taskDesc);
pushTask.setStatus(0); // 0-未觸發(fā)
return pushTaskRepository.save(pushTask);
}
/**
* 刪除推送任務(wù)(同時刪除 Quartz 任務(wù)和業(yè)務(wù)任務(wù))
* @param quartzJobId 任務(wù)ID(添加任務(wù)時返回的 quartzJobId)
*/
public void deletePushTask(String quartzJobId) throws SchedulerException {
// 1. 停止并刪除 Quartz 任務(wù)
JobKey jobKey = JobKey.jobKey(quartzJobId, JOB_GROUP);
TriggerKey triggerKey = TriggerKey.triggerKey(quartzJobId, TRIGGER_GROUP);
scheduler.pauseTrigger(triggerKey); // 暫停觸發(fā)器
scheduler.unscheduleJob(triggerKey); // 移除觸發(fā)器
scheduler.deleteJob(jobKey); // 刪除任務(wù)
// 2. 更新業(yè)務(wù)任務(wù)狀態(tài)為“已取消”
pushTaskRepository.updateStatusByQuartzJobId(2, quartzJobId);
}
/**
* 服務(wù)重啟后恢復(fù)未觸發(fā)的任務(wù)(關(guān)鍵:避免任務(wù)丟失)
*/
public void restoreUnTriggeredTasks() throws SchedulerException {
// 1. 查詢數(shù)據(jù)庫中“未觸發(fā)”且“觸發(fā)時間未到”的任務(wù)
List<PushTask> unTriggeredTasks = pushTaskRepository.findUnTriggeredTasks(LocalDateTime.now());
if (unTriggeredTasks.isEmpty()) {
return;
}
// 2. 重新注冊這些任務(wù)到 Quartz
for (PushTask task : unTriggeredTasks) {
String quartzJobId = task.getQuartzJobId();
LocalDateTime triggerTime = task.getTriggerTime();
String taskDesc = task.getTaskDesc();
// 構(gòu)建 JobDetail
JobDetail jobDetail = JobBuilder.newJob(PushMessageJob.class)
.withIdentity(quartzJobId, JOB_GROUP)
.usingJobData("taskDesc", taskDesc)
.usingJobData("triggerTime", String.valueOf(triggerTime))
.storeDurably()
.build();
// 構(gòu)建 Trigger
String cronExpression = buildCronExpression(triggerTime);
CronTrigger trigger = TriggerBuilder.newTrigger()
.withIdentity(quartzJobId, TRIGGER_GROUP)
.forJob(jobDetail)
.withSchedule(CronScheduleBuilder.cronSchedule(cronExpression))
.startNow()
.build();
// 注冊任務(wù)
scheduler.scheduleJob(jobDetail, trigger);
}
System.out.printf("恢復(fù)未觸發(fā)的推送任務(wù)數(shù)量:%d%n", unTriggeredTasks.size());
}
/**
* 工具方法:將 LocalDateTime 轉(zhuǎn)換為 Cron 表達式(精準到秒)
* Cron 格式:秒 分 時 日 月 周 年(年可選)
* 例:2025-11-01 02:55:00 → 0 55 2 1 11 ? 2025
*/
private String buildCronExpression(LocalDateTime triggerTime) {
int second = triggerTime.getSecond(); // 秒(0)
int minute = triggerTime.getMinute(); // 分(0)
int hour = triggerTime.getHour(); // 時(16)
int day = triggerTime.getDayOfMonth();// 日(21)
int month = triggerTime.getMonthValue();// 月(11,LocalDateTime 直接是 1-12,無需+1)
int year = triggerTime.getYear(); // 年(2025)
// Cron 格式:秒 分 時 日 月 ? 年 → 周字段用 ?,避免與日沖突
return String.format("%d %d %d %d %d ? %d",
second, minute, hour, day, month, year);
}
}7. 啟動時恢復(fù)任務(wù)(監(jiān)聽服務(wù)啟動)
服務(wù)重啟后,自動恢復(fù)數(shù)據(jù)庫中 “未觸發(fā)” 的任務(wù),避免任務(wù)丟失:
import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
@Component
public class TaskRestoreRunner implements ApplicationRunner {
@Autowired
private DynamicQuartzService dynamicQuartzService;
@Override
public void run(ApplicationArguments args) throws SchedulerException {
// 服務(wù)啟動后,恢復(fù)未觸發(fā)的任務(wù)
dynamicQuartzService.restoreUnTriggeredTasks();
}
}8. 測試接口(對外提供添加 / 刪除任務(wù)的入口)
通過 HTTP 接口測試動態(tài)添加、刪除任務(wù):
import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDateTime;
@RestController
@RequestMapping("/push")
public class PushTaskController {
@Autowired
private DynamicQuartzService dynamicQuartzService;
/**
* 添加推送任務(wù)
* @param endTime 結(jié)束時間(格式:yyyy-MM-dd HH:mm:ss,如 2025-11-01 03:00:00)
* @param taskDesc 任務(wù)描述
*/
@PostMapping("/add")
public PushTask addTask(
@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime endTime,
@RequestParam String taskDesc) throws SchedulerException {
return dynamicQuartzService.addPushTask(endTime, taskDesc);
}
/**
* 刪除推送任務(wù)
* @param quartzJobId 任務(wù)ID(添加任務(wù)時返回的 quartzJobId)
*/
@PostMapping("/delete")
public String deleteTask(@RequestParam String quartzJobId) throws SchedulerException {
dynamicQuartzService.deletePushTask(quartzJobId);
return "任務(wù)刪除成功!quartzJobId:" + quartzJobId;
}
}三、核心功能測試
1. 添加任務(wù)測試
發(fā)送 POST 請求:
plaintext http://localhost:8080/push/add?endTime=2025-11-01
03:00:00&taskDesc=訂單123超時提醒
- 后臺會自動計算觸發(fā)時間:2025-11-01 02:55:00;
- 數(shù)據(jù)庫 push_task 表會新增一條記錄,狀態(tài)為 0(未觸發(fā));
- Quartz 會創(chuàng)建對應(yīng)的 Job 和 Trigger,到 02:55 自動觸發(fā)推送。
2. 觸發(fā)效果
到觸發(fā)時間后,控制臺會輸出:
[推送通知] 任務(wù)描述:訂單123超時提醒,觸發(fā)時間:2025-11-01T02:55,內(nèi)容:即將到達結(jié)束時間(提前5分鐘提醒)
同時 push_task 表中該任務(wù)的狀態(tài)會更新為 1(已觸發(fā))。
3. 刪除任務(wù)測試
發(fā)送 POST 請求:
http://localhost:8080/push/delete?quartzJobId=PUSH_JOB_xxx(添加任務(wù)時返回的
quartzJobId)
- Quartz 會刪除對應(yīng)的 Job 和 Trigger;
- 數(shù)據(jù)庫中任務(wù)狀態(tài)更新為 2(已取消)。
四、關(guān)鍵特性說明
1. 動態(tài)性
- 支持任意多條結(jié)束時間:調(diào)用 addPushTask 方法可添加多個任務(wù),每個任務(wù)獨立觸發(fā);
- 結(jié)束時間不確定:無需提前配置 Cron 表達式,傳入 LocalDateTime 即可自動生成觸發(fā)規(guī)則。
2. 可靠性
- 持久化:任務(wù)信息存儲在數(shù)據(jù)庫,服務(wù)重啟后自動恢復(fù)未觸發(fā)的任務(wù);
- 失敗重試:推送失敗時拋出異常,Quartz 會根據(jù)配置的策略重試(默認重試 3 次);
- 集群支持:修改 application.yml 中 quartz.properties.org.quartz.jobStore.isClustered=true,即可支持集群部署(避免重復(fù)觸發(fā))。
3. 靈活性
- 提前時間可配置:將 minusMinutes(5) 改為參數(shù)(如 minusMinutes(提前分鐘數(shù))),支- - 持動態(tài)調(diào)整提前提醒時間;
- 推送邏輯可擴展:修改 doPush 方法,整合短信、APP 推送(極光 / 個推)、郵件等任意渠道。
4. 時間校驗
- 避免添加已過期的任務(wù)(結(jié)束時間早于當前時間);
- 避免提前 5 分鐘后仍過期的任務(wù)(如結(jié)束時間為當前時間 + 3 分鐘,提前 5 分鐘則已過期)。
五、生產(chǎn)環(huán)境優(yōu)化建議
1.任務(wù)監(jiān)控:通過 Quartz 提供的 API 監(jiān)控任務(wù)狀態(tài)(如查詢所有任務(wù)、觸發(fā)次數(shù)、失敗次數(shù)),或集成 Prometheus + Grafana 可視化監(jiān)控;
2.過期任務(wù)清理:定時清理數(shù)據(jù)庫中 “已觸發(fā)” 或 “已取消” 的任務(wù)(避免表數(shù)據(jù)過大);
3.推送異步化:如果推送邏輯耗時較長(如調(diào)用第三方接口),可在 doPush 中發(fā)送 MQ 消息,由消費者異步處理推送(解耦定時任務(wù)和推送邏輯);
4.日志增強:記錄推送結(jié)果(成功 / 失?。┑饺罩疚募驍?shù)據(jù)庫,方便問題排查;
5.權(quán)限控制:對 /push/add 和 /push/delete 接口添加權(quán)限校驗(如 Token 驗證),避免惡意操作。
總結(jié)
本方案通過 Quartz 動態(tài)任務(wù) + 數(shù)據(jù)庫持久化,完美滿足 “多條不確定結(jié)束時間 + 提前 5 分鐘推送” 的需求,核心優(yōu)勢:
- 動態(tài)靈活:支持任意結(jié)束時間,無需提前配置;
- 可靠穩(wěn)定:任務(wù)持久化、服務(wù)重啟恢復(fù)、失敗重試;
- 易于擴展:支持多推送渠道、集群部署、監(jiān)控告警。
如果需要輕量級方案(無需持久化,服務(wù)重啟后任務(wù)可丟失),也可使用 Spring Scheduler + 內(nèi)存緩存,但生產(chǎn)環(huán)境建議優(yōu)先選擇本方案(可靠性更高)
到此這篇關(guān)于SpringBoot整合 Quartz實現(xiàn)定時推送實戰(zhàn)指南的文章就介紹到這了,更多相關(guān)SpringBoot Quartz定時推送內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
maven?springboot如何將jar包打包到指定目錄
這篇文章主要介紹了maven?springboot如何將jar包打包到指定目錄,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-12-12
SpringBoot中@ComponentScan的使用詳解
這篇文章主要介紹了SpringBoot中@ComponentScan的使用詳解,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-11-11
解決Android Studio安裝后運行出錯dose not...和Internal error...
這篇文章主要介紹了解決Android Studio安裝后運行出錯dose not...和Internal error...的相關(guān)資料,需要的朋友可以參考下2017-03-03

