思路
- 将当前时间戳和延时时间相加存入zset
- 轮询获取score在0-当前时间戳的第一个元素
- 删除该元素并执行对应的业务。
相关命令
-
zadd key [nx|xx] [ch] [incr] score member [score member …]
添加成员,如果member已存在,则更新score,可添加多个键值对
可选参数:
- nx:当某成员存在时,不更新对应的score,只添加新成员
- xx:只更新存在的成员的score,不添加新成员
- ch:修改返回值为发生变化的总数
- incr:给对应的成员增加对应的score
-
zrangebyscore key min max [withscores] [limit offset count]
获取分数在min和max之间的元素,分数从低到高排序。
可选参数:
withscores:不仅会返回member,还会返回score
limit offset cout:跟mysql 的limit 作用相同,类似分页的作用
-
zrem key member [member …]
删除key中指定的成员。
示例
127.0.0.1:6379> zadd key 1 member 2 member1
(integer) 2
127.0.0.1:6379> zrangebyscore key 0 2 limit 0 1
1) "member"
127.0.0.1:6379> zrem key member
(integer) 1
实战
1、引入redis依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2、修改配置
spring:
redis:
host: localhost #主机地址
port: 6379 #端口号
password: #密码(没有则不写)
jedis:
pool:
max-active: 8
max-wait: -1
max-idle: 500
min-idle: 0
lettuce:
shutdown-timeout: 0
3、启动类加注解
import org.springframework.scheduling.annotation.EnableScheduling;
@EnableScheduling
4、编写RedisConfig文件
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate redisTemplate = new RedisTemplate();
redisTemplate.setConnectionFactory(redisConnectionFactory);
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
@Bean
public RedisCacheManager redisCacheManager(RedisTemplate redisTemplate) {
RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisTemplate.getConnectionFactory());
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisTemplate.getValueSerializer()));
return new RedisCacheManager(redisCacheWriter, redisCacheConfiguration);
}
}
5、编写监听类
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.util.Set;
import java.util.UUID;
@Component
public class RedisDelayQueue {
@Autowired
private RedisTemplate redisTemplate;
/**
* 监听延迟消息
*/
@Scheduled(cron = "0/2 * * * * ?")
public void listenDelayLoop() {
// 获取一个到点的消息
Set<Integer> set = redisTemplate.opsForZSet().rangeByScore("cancelOrder", 0, System.currentTimeMillis(),0,1);
// 如果没有,就等等
if (set.isEmpty()) {
// 继续执行
return;
}
// 获取具体消息的key
Integer it = set.iterator().next();
// 删除成功
if (redisTemplate.opsForZSet().remove("cancelOrder", it) > 0) {
// 业务
}
}
}
6、测试
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
@SpringBootTest
class DemoApplicationTests {
@Autowired
private RedisTemplate redisTemplate;
@Test
public void test(){
int delayTime=15*60*1000;
redisTemplate.opsForZSet().add("cancelOrder",1,System.currentTimeMillis()+delayTime);
}
}
以上就是使用zset来完成取消订单的步骤,使用这个方式有很多弊端,比如需要考虑到Redis的持久化,还有业务执行时报错了消息会丢失,需要多建一个表来解决这种问题。