最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot整合 Quartz實現(xiàn)定時推送實戰(zhàn)指南

 更新時間:2025年12月10日 14:54:07   作者:喝汽水的貓^  
文章介紹了Spring Boot中使用Quartz動態(tài)定時任務(wù)和任務(wù)持久化實現(xiàn)多條不確定結(jié)束時間并提前N分鐘推送的方案,本文結(jié)合實例代碼給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧

前言

根據(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ù)” 的標(biāo)準(zhǔn)解決方案,支持復(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 的核心 “大腦”,負(fù)責(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ù)庫),默認(rèn)是 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("消息推送失?。uartzJobId:{}", 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é)束時間必須晚于當(dāng)前時間!");
        }
        if (triggerTime.isBefore(now) || triggerTime.isEqual(now)) {
            throw new IllegalArgumentException("觸發(fā)時間必須晚于當(dāng)前時間!");
        }
        // 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 表達式(精準(zhǔn)到秒)
     * 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ù)配置的策略重試(默認(rèn)重試 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é)束時間早于當(dāng)前時間);
  • 避免提前 5 分鐘后仍過期的任務(wù)(如結(jié)束時間為當(dāng)前時間 + 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)文章

  • Java中的RoundingMode舍入模式詳解

    Java中的RoundingMode舍入模式詳解

    Java中的RoundingMode枚舉類用于控制高精度數(shù)值處理時的舍入行為,提供多種舍入模式,本文給大家介紹Java的RoundingMode舍入模式,感興趣的朋友跟隨小編一起看看吧
    2026-01-01
  • Java類的加載連接和初始化實例分析

    Java類的加載連接和初始化實例分析

    這篇文章主要介紹了Java類的加載連接和初始化,結(jié)合具體實例形式分析了java類的加載、連接、初始化相關(guān)原理與實現(xiàn)技巧,需要的朋友可以參考下
    2019-07-07
  • Java 8中default方法能做什么?不能做什么?

    Java 8中default方法能做什么?不能做什么?

    這篇文章主要給大家介紹了關(guān)于Java 8中default方法能做什么?不能做什么?文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。
    2018-04-04
  • springboot使用@ConfigurationProperties實現(xiàn)自動綁定配置參數(shù)屬性

    springboot使用@ConfigurationProperties實現(xiàn)自動綁定配置參數(shù)屬性

    這篇文章主要介紹了springboot使用@ConfigurationProperties實現(xiàn)自動綁定配置參數(shù)屬性,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2025-05-05
  • 用Java驗證pdf文件的電子章簽名

    用Java驗證pdf文件的電子章簽名

    這篇文章主要介紹了如何用Java驗證pdf文件的電子章簽名,幫助大家更好的理解和使用Java,感興趣的朋友可以了解下
    2020-12-12
  • 詳解Java子線程異常時主線程事務(wù)如何回滾

    詳解Java子線程異常時主線程事務(wù)如何回滾

    如果主線程向線程池提交了一個任務(wù),如果執(zhí)行這個任務(wù)過程中發(fā)生了異常,如何讓主線程捕獲到該異常并且進行事務(wù)的回滾?本篇文章帶給你答案
    2022-03-03
  • mybatis-plus實現(xiàn)打印完整sql語句

    mybatis-plus實現(xiàn)打印完整sql語句

    這篇文章主要介紹了mybatis-plus實現(xiàn)打印完整sql語句方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-07-07
  • Java線程中的interrupt方法解讀

    Java線程中的interrupt方法解讀

    這篇文章主要介紹了Java線程中的interrupt方法解讀,Java中的interrupt是一種線程間通信的機制,用于請求中斷線程的執(zhí)行。當(dāng)一個線程調(diào)用另一個線程的interrupt()方法時,被調(diào)用線程會收到一個中斷信號,可以根據(jù)需要做出相應(yīng)的處理,需要的朋友可以參考下
    2023-10-10
  • java的jps命令使用詳解

    java的jps命令使用詳解

    這篇文章介紹了java的jps命令使用詳解,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-12-12
  • Java設(shè)計模式之策略模式示例詳解

    Java設(shè)計模式之策略模式示例詳解

    這篇文章主要為大家詳細(xì)介紹了Java的策略模式,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-03-03

最新評論

辛集市| 博乐市| 商都县| 石屏县| 淮阳县| 大竹县| 临高县| 抚顺县| 河间市| 南靖县| 定陶县| 河北区| 孟连| 新源县| 北碚区| 获嘉县| 无极县| 河间市| 珠海市| 银川市| 河源市| 石门县| 天镇县| 汝州市| 客服| 勃利县| 广饶县| 靖边县| 太谷县| 永新县| 内江市| 苍山县| 拉孜县| 保山市| 贵德县| 常山县| 云南省| 桂阳县| 临海市| 灵台县| 洛川县|