Springboot使用Redis實現定時任務的三種方式
一、基于 Redis 鍵空間通知(適合精確延時任務)
原理:利用 Redis 的鍵過期事件(EXPIRE)觸發(fā)任務執(zhí)行,通過監(jiān)聽 __keyevent@*__:expired 通道捕獲事件。
步驟:
啟用 Redis 鍵空間通知(redis.conf 或運行時配置):
CONFIG SET notify-keyspace-events Ex
Spring Boot 監(jiā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è)務鍵
System.out.println("執(zhí)行任務: " + 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;
}
}
調度任務(設置帶過期時間的鍵):
@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 有序集合輪詢(適合批量定時任務)
原理:將任務執(zhí)行時間作為 ZSET 的 score,通過定時任務查詢到期的任務并執(zhí)行。
步驟:
添加任務到 ZSET:
public void addTask(String taskId, Instant executeTime) {
stringRedisTemplate.opsForZSet().add("scheduled_tasks", taskId,
executeTime.getEpochSecond());
}
定時掃描并執(zhí)行任務(每分鐘輪詢):
@Scheduled(cron = "0 * * * * *") // 每分鐘執(zhí)行
public void pollTasks() {
long now = Instant.now().getEpochSecond();
Set<String> tasks = stringRedisTemplate.opsForZSet()
.rangeByScore("scheduled_tasks", 0, now); // 獲取所有到期任務
for (String task : tasks) {
System.out.println("執(zhí)行任務: " + task);
// 執(zhí)行后移除任務
stringRedisTemplate.opsForZSet().remove("scheduled_tasks", task);
}
}
優(yōu)點:避免鍵空間通知的丟失風險,適合任務量大的場景。
三、基于 Redis 分布式鎖(防集群任務重復執(zhí)行)
原理:在分布式環(huán)境中,通過 Redis 鎖確保同一時間只有一個實例執(zhí)行定時任務。
代碼示例:
@Component
public class DistributedTask {
@Autowired
private StringRedisTemplate redisTemplate;
private static final String LOCK_KEY = "TASK_LOCK:ORDER_CLEAN";
@Scheduled(cron = "0 0 3 * * ?") // 每天凌晨3點執(zhí)行
public void dailyTask() {
Boolean locked = redisTemplate.opsForValue()
.setIfAbsent(LOCK_KEY, "locked", Duration.ofMinutes(10));
if (Boolean.TRUE.equals(locked)) {
try {
cleanExpiredOrders(); // 執(zhí)行核心任務
} finally {
redisTemplate.delete(LOCK_KEY); // 釋放鎖(可選)
}
}
}
}
關鍵點:
- 使用
setIfAbsent原子操作獲取鎖,避免并發(fā)沖突。 - 鎖自動過期防止死鎖(如任務執(zhí)行超時)。
??版本要求??:
??Spring Data Redis ≥ 2.3.0??:該版本引入了 setIfAbsent(key, value, duration) 方法,支持??原子性設置鍵值+過期時間??(對應 Redis 的 SET key value NX EX seconds 命令)
四、方案對比
| 方案 | 適用場景 | 注意事項 |
|---|---|---|
| 鍵空間通知 | 精確延時任務(如30分鐘后關單) | 需配置 Redis,事件可能丟失 |
| 有序集合輪詢 | 批量任務、高可靠性場景 | 需自行處理任務分頁和重試 |
| 分布式鎖 | 集群環(huán)境防重復執(zhí)行(如日報生成) | 鎖超時時間需大于任務執(zhí)行時間 |
*補充:
- 關鍵業(yè)務(如支付超時)建議結合 數據庫日志+重試機制 補償;
- 高頻任務優(yōu)先選 ZSET 輪詢,避免鍵空間通知的性能瓶頸;
- 分布式鎖的鎖鍵需包含業(yè)務標識(如
LOCK_KEY:業(yè)務名)。
到此這篇關于Springboot使用Redis實現定時任務的三種方式的文章就介紹到這了,更多相關Springboot Redis 定時任務內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
如何在SpringBoot中使用logback優(yōu)化異常堆棧的輸出詳解
最近項目中整合了logback,所以下面這篇文章主要給大家介紹了關于如何在SpringBoot中使用logback優(yōu)化異常堆棧的輸出,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下2022-05-05
java如何實時動態(tài)獲取properties文件的內容
這篇文章主要介紹了java如何實時動態(tài)獲取properties文件的內容,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-09-09
淺談SpringMVC中Interceptor和Filter區(qū)別
這篇文章主要介紹了淺談SpringMVC中Interceptor和Filter區(qū)別,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-04-04
SpringBoot+Thymeleaf靜態(tài)資源的映射規(guī)則說明
這篇文章主要介紹了SpringBoot+Thymeleaf靜態(tài)資源的映射規(guī)則說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-11-11

