Springboot使用Redis實(shí)現(xiàn)定時任務(wù)的三種方式
一、基于 Redis 鍵空間通知(適合精確延時任務(wù))
原理:利用 Redis 的鍵過期事件(EXPIRE
)觸發(fā)任務(wù)執(zhí)行,通過監(jiān)聽 __keyevent@*__:expired
通道捕獲事件。
步驟:
啟用 Redis 鍵空間通知(redis.conf 或運(yùn)行時配置):
CONFIG SET notify-keyspace-events Ex
Spring Boot 監(jiān)聽器實(shí)現(xiàn):
@Component public class KeyExpiredListener extends KeyExpirationEventMessageListener { public KeyExpiredListener(RedisMessageListenerContainer listenerContainer) { super(listenerContainer); } @Override public void onMessage(Message message, byte[] pattern) { String expiredKey = new String(message.getBody()); if (expiredKey.startsWith("task:")) { // 過濾業(yè)務(wù)鍵 System.out.println("執(zhí)行任務(wù): " + expiredKey); // 例如:task:123 過期時執(zhí)行訂單超時邏輯 } } }
注冊監(jiān)聽器:
@Configuration public class RedisConfig { @Bean RedisMessageListenerContainer container(RedisConnectionFactory factory, KeyExpiredListener listener) { RedisMessageListenerContainer container = new RedisMessageListenerContainer(); container.setConnectionFactory(factory); container.addMessageListener(listener, new PatternTopic("__keyevent@*__:expired")); return container; } }
調(diào)度任務(wù)(設(shè)置帶過期時間的鍵):
@Service public class TaskScheduler { @Autowired private StringRedisTemplate redisTemplate; public void scheduleTask(String taskId, long delaySeconds) { redisTemplate.opsForValue().set("task:" + taskId, "data", delaySeconds, TimeUnit.SECONDS); // 鍵在 delaySeconds 秒后過期 } }
注意:需在 Redis 配置中開啟 notify-keyspace-events Ex
。
二、基于 Redis 有序集合輪詢(適合批量定時任務(wù))
原理:將任務(wù)執(zhí)行時間作為 ZSET
的 score
,通過定時任務(wù)查詢到期的任務(wù)并執(zhí)行。
步驟:
添加任務(wù)到 ZSET:
public void addTask(String taskId, Instant executeTime) { stringRedisTemplate.opsForZSet().add("scheduled_tasks", taskId, executeTime.getEpochSecond()); }
定時掃描并執(zhí)行任務(wù)(每分鐘輪詢):
@Scheduled(cron = "0 * * * * *") // 每分鐘執(zhí)行 public void pollTasks() { long now = Instant.now().getEpochSecond(); Set<String> tasks = stringRedisTemplate.opsForZSet() .rangeByScore("scheduled_tasks", 0, now); // 獲取所有到期任務(wù) for (String task : tasks) { System.out.println("執(zhí)行任務(wù): " + task); // 執(zhí)行后移除任務(wù) stringRedisTemplate.opsForZSet().remove("scheduled_tasks", task); } }
優(yōu)點(diǎn):避免鍵空間通知的丟失風(fēng)險,適合任務(wù)量大的場景。
三、基于 Redis 分布式鎖(防集群任務(wù)重復(fù)執(zhí)行)
原理:在分布式環(huán)境中,通過 Redis 鎖確保同一時間只有一個實(shí)例執(zhí)行定時任務(wù)。
代碼示例:
@Component public class DistributedTask { @Autowired private StringRedisTemplate redisTemplate; private static final String LOCK_KEY = "TASK_LOCK:ORDER_CLEAN"; @Scheduled(cron = "0 0 3 * * ?") // 每天凌晨3點(diǎn)執(zhí)行 public void dailyTask() { Boolean locked = redisTemplate.opsForValue() .setIfAbsent(LOCK_KEY, "locked", Duration.ofMinutes(10)); if (Boolean.TRUE.equals(locked)) { try { cleanExpiredOrders(); // 執(zhí)行核心任務(wù) } finally { redisTemplate.delete(LOCK_KEY); // 釋放鎖(可選) } } } }
關(guān)鍵點(diǎn):
- 使用
setIfAbsent
原子操作獲取鎖,避免并發(fā)沖突。 - 鎖自動過期防止死鎖(如任務(wù)執(zhí)行超時)。
??版本要求??:
??Spring Data Redis ≥ 2.3.0??:該版本引入了 setIfAbsent(key, value, duration) 方法,支持??原子性設(shè)置鍵值+過期時間??(對應(yīng) Redis 的 SET key value NX EX seconds 命令)
四、方案對比
方案 | 適用場景 | 注意事項 |
---|---|---|
鍵空間通知 | 精確延時任務(wù)(如30分鐘后關(guān)單) | 需配置 Redis,事件可能丟失 |
有序集合輪詢 | 批量任務(wù)、高可靠性場景 | 需自行處理任務(wù)分頁和重試 |
分布式鎖 | 集群環(huán)境防重復(fù)執(zhí)行(如日報生成) | 鎖超時時間需大于任務(wù)執(zhí)行時間 |
*補(bǔ)充:
- 關(guān)鍵業(yè)務(wù)(如支付超時)建議結(jié)合 數(shù)據(jù)庫日志+重試機(jī)制 補(bǔ)償;
- 高頻任務(wù)優(yōu)先選 ZSET 輪詢,避免鍵空間通知的性能瓶頸;
- 分布式鎖的鎖鍵需包含業(yè)務(wù)標(biāo)識(如
LOCK_KEY:業(yè)務(wù)名
)。
到此這篇關(guān)于Springboot使用Redis實(shí)現(xiàn)定時任務(wù)的三種方式的文章就介紹到這了,更多相關(guān)Springboot Redis 定時任務(wù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Redis定時監(jiān)控與數(shù)據(jù)處理的實(shí)踐指南
- Redis拓展之定時消息通知實(shí)現(xiàn)詳解
- Redis?延時任務(wù)實(shí)現(xiàn)及與定時任務(wù)區(qū)別詳解
- Spring boot詳解緩存redis實(shí)現(xiàn)定時過期方法
- Redis定時任務(wù)原理的實(shí)現(xiàn)
- Python定時從Mysql提取數(shù)據(jù)存入Redis的實(shí)現(xiàn)
- Spring Boot監(jiān)聽Redis Key失效事件實(shí)現(xiàn)定時任務(wù)的示例
- 基于redis實(shí)現(xiàn)定時任務(wù)的方法詳解
相關(guān)文章
springboot整合prometheus實(shí)現(xiàn)資源監(jiān)控的詳細(xì)步驟
Spring Boot與Prometheus的整合可以實(shí)現(xiàn)對Spring Boot應(yīng)用的實(shí)時監(jiān)控,有助于更好地維護(hù)應(yīng)用的性能,本文給大家介紹springboot整合prometheus實(shí)現(xiàn)資源監(jiān)控的詳細(xì)步驟,感興趣的朋友跟隨小編一起看看吧2024-11-11淺談java String.split丟失結(jié)尾空字符串的問題
下面小編就為大家?guī)硪黄獪\談java String.split丟失結(jié)尾空字符串的問題。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-02-02springMVC幾種頁面跳轉(zhuǎn)方式小結(jié)
本篇文章主要介紹了springMVC 幾種頁面跳轉(zhuǎn)方式,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-02-02SpringBoot開發(fā)案例之配置Druid數(shù)據(jù)庫連接池的示例
本篇文章主要介紹了SpringBoot開發(fā)案例之配置Druid數(shù)據(jù)庫連接池的示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-03-03深入理解java中的synchronized關(guān)鍵字
這篇文章主要介紹了java中的synchronized關(guān)鍵字,有需要的朋友可以參考一下2013-12-12Java使用WebView實(shí)現(xiàn)桌面程序的技術(shù)指南
在現(xiàn)代軟件開發(fā)中,許多應(yīng)用需要在桌面程序中嵌入 Web 頁面,例如,你可能需要在 Java 桌面應(yīng)用中嵌入一部分 Web 前端,或者加載一個 HTML5 界面以增強(qiáng)用戶體驗(yàn),所以本文給大家介紹了Java使用WebView實(shí)現(xiàn)桌面程序的技術(shù)指南,需要的朋友可以參考下2025-05-05MyBatis-Plus實(shí)現(xiàn)優(yōu)雅處理JSON字段映射
默認(rèn)情況下,MyBatis-Plus 是不支持直接映射 JSON 類型的,這時候就需要借助其他的方法,下面小編就來和大家講講MyBatis-Plus如何優(yōu)雅處理JSON字段映射吧2025-04-04Jmeter對響應(yīng)數(shù)據(jù)實(shí)現(xiàn)斷言代碼實(shí)例
這篇文章主要介紹了Jmeter對響應(yīng)數(shù)據(jù)實(shí)現(xiàn)斷言代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-09-09