添加必要的依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-scheduling</artifactId>
</dependency>
确保你的主类有 @EnableScheduling
注解:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication
@EnableScheduling
public class DistributedScheduledTaskApplication {
public static void main(String[] args) {
SpringApplication.run(DistributedScheduledTaskApplication.class, args);
}
}
创建一个Redis分布式锁类:
RedisDistributedLock: 这个类负责尝试获取和释放Redis分布式锁。它使用了 setIfAbsent
方法来尝试设置锁,如果锁过期则可以重置。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.time.Instant;
import java.util.concurrent.TimeUnit;
@Component
public class RedisDistributedLock {
@Autowired
private RedisTemplate<String, String> redisTemplate;
private static final String LOCK_PREFIX = "scheduled_task_lock:";
private static final int LOCK_EXPIRE_TIME = 300; // 5 minutes
public boolean tryAcquireLock(String taskName, String nodeIdentifier) {
String lockKey = LOCK_PREFIX + taskName;
long expirationTime = System.currentTimeMillis() + LOCK_EXPIRE_TIME * 1000;
String expirationTimeString = String.valueOf(expirationTime);
if (Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(lockKey, expirationTimeString, LOCK_EXPIRE_TIME, TimeUnit.SECONDS))) {
return true;
} else {
String currentLockValue = redisTemplate.opsForValue().get(lockKey);
if (currentLockValue != null && Long.parseLong(currentLockValue) < System.currentTimeMillis()) {
String oldValue = redisTemplate.opsForValue().getAndSet(lockKey, expirationTimeString);
if (oldValue != null && oldValue.equals(currentLockValue)) {
return true;
}
}
}
return false;
}
public void releaseLock(String taskName, String nodeIdentifier) {
String lockKey = LOCK_PREFIX + taskName;
String currentLockValue = redisTemplate.opsForValue().get(lockKey);
if (currentLockValue != null && Long.parseLong(currentLockValue) > System.currentTimeMillis()) {
redisTemplate.delete(lockKey);
}
}
}
创建定时任务类:
DistributedScheduledTask: 这个类包含定时任务的逻辑。 @Scheduled
注解用于定时执行任务。如果当前节点成功获取锁,则执行任务并在任务完成后释放锁。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component
public class DistributedScheduledTask {
@Autowired
private RedisDistributedLock redisDistributedLock;
private static final String TASK_NAME = "my_scheduled_task";
private static final String NODE_IDENTIFIER = "node-1"; // 节点标识,可以配置成动态,或者配置文件读取
@Scheduled(fixedRate = 15000) // 15秒运行一次
public void executeTask() {
if (redisDistributedLock.tryAcquireLock(TASK_NAME, NODE_IDENTIFIER)) {
try {
System.out.println("Lock acquired by " + NODE_IDENTIFIER + ", executing task at " + Instant.now());
performTask();
} finally {
redisDistributedLock.releaseLock(TASK_NAME, NODE_IDENTIFIER);
}
} else {
System.out.println(NODE_IDENTIFIER + " failed to acquire lock, task is being handled by another node.");
}
}
private void performTask() {
// 定时任务方法
System.out.println("Performing task at " + Instant.now());
// 模拟任务时长
try {
Thread.sleep(5000); // 执行5s
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
这段代码可以确保在一个分布式系统中只有一个节点在某一时刻执行定时任务。