我需要使用 redis zset 来模拟任务队列, zset 的key为任务 id,value 为任务待处理时间戳。请根据以下需求,生成使用 spring redistTemplate 操作 redis的 java 代码:
你可以使用Spring的RedisTemplate来操作Redis,实现上述需求。以下是针对每个需求的Java代码:
生成添加任务 id 的方法,value 默认为当前时间戳加上 60000 毫秒:
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
@Service
public class TaskQueueService {
private final RedisTemplate<String, String> redisTemplate;
private static final String ZSET_KEY = "task_queue";
public TaskQueueService(RedisTemplate<String, String> redisTemplate) {
this.redisTemplate = redisTemplate;
}
public void addTask(String taskId) {
long currentTimeMillis = System.currentTimeMillis();
long delayedTimeMillis = currentTimeMillis + 60000; // 默认延迟 60000 毫秒
redisTemplate.opsForZSet().add(ZSET_KEY, taskId, delayedTimeMillis);
}
}
生成删除任务 id 的方法:
public void removeTask(String taskId) {
redisTemplate.opsForZSet().remove(ZSET_KEY, taskId);
}
生成获取任务 id 的方法,每次请求获得 value 最小且 value 小于当前时间的第一个任务 id,并同时更新 value 值为当前时间戳加 60000 毫秒。这需要使用 Lua 脚本来实现原子操作:
public String getAndRenewTask() {
String luaScript = "local task = redis.call('ZRANGEBYSCORE', KEYS[1], '-inf', ARGV[1], 'LIMIT', 0, 1)\n" +
"if #task > 0 then\n" +
" local taskId = task[1]\n" +
" redis.call('ZREM', KEYS[1], taskId)\n" +
" local newTime = ARGV[1] + " + 60000 + "\n" +
" redis.call('ZADD', KEYS[1], newTime, taskId)\n" +
" return taskId\n" +
"else\n" +
" return nil\n" +
"end";
List<String> keys = Collections.singletonList(ZSET_KEY);
long currentTimeMillis = System.currentTimeMillis();
Object taskId = redisTemplate.execute(new DefaultRedisScript<>(luaScript, String.class), keys, currentTimeMillis);
return taskId != null ? taskId.toString() : null;
}
上述代码中,我们使用 Lua 脚本进行了原子操作,首先获取并移除符合条件的任务 id,然后重新添加一个新的带有更新后的时间戳的任务 id。这确保了获取和更新操作的原子性。