Springboot使用Redis實(shí)現(xiàn)定時(shí)任務(wù)的三種方式
一、基于 Redis 鍵空間通知(適合精確延時(shí)任務(wù))
原理:利用 Redis 的鍵過期事件(EXPIRE)觸發(fā)任務(wù)執(zhí)行,通過監(jiān)聽 __keyevent@*__:expired 通道捕獲事件。
步驟:
啟用 Redis 鍵空間通知(redis.conf 或運(yùn)行時(shí)配置):
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 過期時(shí)執(zhí)行訂單超時(shí)邏輯
}
}
}
注冊(cè)監(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è)置帶過期時(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 有序集合輪詢(適合批量定時(shí)任務(wù))
原理:將任務(wù)執(zhí)行時(shí)間作為 ZSET 的 score,通過定時(shí)任務(wù)查詢到期的任務(wù)并執(zhí)行。
步驟:
添加任務(wù)到 ZSET:
public void addTask(String taskId, Instant executeTime) {
stringRedisTemplate.opsForZSet().add("scheduled_tasks", taskId,
executeTime.getEpochSecond());
}
定時(shí)掃描并執(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)險(xiǎn),適合任務(wù)量大的場(chǎng)景。
三、基于 Redis 分布式鎖(防集群任務(wù)重復(fù)執(zhí)行)
原理:在分布式環(huán)境中,通過 Redis 鎖確保同一時(shí)間只有一個(gè)實(shí)例執(zhí)行定時(shí)任務(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ā)沖突。 - 鎖自動(dòng)過期防止死鎖(如任務(wù)執(zhí)行超時(shí))。
??版本要求??:
??Spring Data Redis ≥ 2.3.0??:該版本引入了 setIfAbsent(key, value, duration) 方法,支持??原子性設(shè)置鍵值+過期時(shí)間??(對(duì)應(yīng) Redis 的 SET key value NX EX seconds 命令)
四、方案對(duì)比
| 方案 | 適用場(chǎng)景 | 注意事項(xiàng) |
|---|---|---|
| 鍵空間通知 | 精確延時(shí)任務(wù)(如30分鐘后關(guān)單) | 需配置 Redis,事件可能丟失 |
| 有序集合輪詢 | 批量任務(wù)、高可靠性場(chǎng)景 | 需自行處理任務(wù)分頁和重試 |
| 分布式鎖 | 集群環(huán)境防重復(fù)執(zhí)行(如日?qǐng)?bào)生成) | 鎖超時(shí)時(shí)間需大于任務(wù)執(zhí)行時(shí)間 |
*補(bǔ)充:
- 關(guān)鍵業(yè)務(wù)(如支付超時(shí))建議結(jié)合 數(shù)據(jù)庫日志+重試機(jī)制 補(bǔ)償;
- 高頻任務(wù)優(yōu)先選 ZSET 輪詢,避免鍵空間通知的性能瓶頸;
- 分布式鎖的鎖鍵需包含業(yè)務(wù)標(biāo)識(shí)(如
LOCK_KEY:業(yè)務(wù)名)。
到此這篇關(guān)于Springboot使用Redis實(shí)現(xiàn)定時(shí)任務(wù)的三種方式的文章就介紹到這了,更多相關(guān)Springboot Redis 定時(shí)任務(wù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Redis定時(shí)監(jiān)控與數(shù)據(jù)處理的實(shí)踐指南
- Redis拓展之定時(shí)消息通知實(shí)現(xiàn)詳解
- Redis?延時(shí)任務(wù)實(shí)現(xiàn)及與定時(shí)任務(wù)區(qū)別詳解
- Spring boot詳解緩存redis實(shí)現(xiàn)定時(shí)過期方法
- Redis定時(shí)任務(wù)原理的實(shí)現(xiàn)
- Python定時(shí)從Mysql提取數(shù)據(jù)存入Redis的實(shí)現(xiàn)
- Spring Boot監(jiān)聽Redis Key失效事件實(shí)現(xiàn)定時(shí)任務(wù)的示例
- 基于redis實(shí)現(xiàn)定時(shí)任務(wù)的方法詳解
相關(guān)文章
如何在SpringBoot中使用logback優(yōu)化異常堆棧的輸出詳解
最近項(xiàng)目中整合了logback,所以下面這篇文章主要給大家介紹了關(guān)于如何在SpringBoot中使用logback優(yōu)化異常堆棧的輸出,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-05-05
springboot前后臺(tái)數(shù)據(jù)交互的示例代碼
這篇文章主要介紹了springboot前后臺(tái)數(shù)據(jù)交互的示例代碼,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-10-10
java如何實(shí)時(shí)動(dòng)態(tài)獲取properties文件的內(nèi)容
這篇文章主要介紹了java如何實(shí)時(shí)動(dòng)態(tài)獲取properties文件的內(nèi)容,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09
淺談SpringMVC中Interceptor和Filter區(qū)別
這篇文章主要介紹了淺談SpringMVC中Interceptor和Filter區(qū)別,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2019-04-04
SpringBoot+Thymeleaf靜態(tài)資源的映射規(guī)則說明
這篇文章主要介紹了SpringBoot+Thymeleaf靜態(tài)資源的映射規(guī)則說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11

