1.场景
不知道各位开发者们在开发过程中有没有遇到过这样的场景,就是有一条数据进来了,要设置超时时间,比如我这里有一条待办的数据,48小时还未处理的话,这条数据要变成超时的状态,此时不光要修改状态,还要发信息通知他消息超时未处理。
此时,你有如下的解决思路:
- 定时任务:每隔多长时间轮询一遍数据库,根据创建时间进行判断,超过48小时的进行处理,缺点显而易见,超时处理的不及时,时间设置的过短,可能上一次还没遍历完,下一次又开始了,时间设置过长,可能早就超时了,直到现在你才提醒用户超时
除此之外还有很多种,比如数据库写个触发器,通过MQ等等,但是这些都有很大的缺点。
2.Redis延时队列
2.1 引入依赖
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.16.4</version>
</dependency>
2.2 注入RedisClient
@Configuration
public class RedissonConfig {
@Value("${spring.redis.host}")
private String host;
@Value("${spring.redis.port}")
private String port;
@Value("${spring.redis.password}")
private String password;
@Value("${spring.redis.database}")
private Integer database;
@Bean()
public RedissonClient redissonClient() {
Config config = new Config();
SingleServerConfig serversConfig = config.useSingleServer();
serversConfig.setAddress("redis://" + host + ":" + port);
if (StringUtils.isNotEmpty(password)){
serversConfig.setPassword(password);
}
serversConfig.setDatabase(database);
return Redisson.create(config);
}
}
3. 注入延时队列Bean
@Configuration
public class RedissonQueueConfig {
private final String queueName = "queue";
@Bean
public RBlockingQueue<String> rBlockingQueue(RedissonClient redissonClient) {
return redissonClient.getBlockingDeque(queueName);
}
@Bean(name = "rDelayedQueue")
public RDelayedQueue<String> rDelayedQueue(RedissonClient redissonClient,
@Qualifier("rBlockingQueue") RBlockingQueue<String> blockingQueue) {
return redissonClient.getDelayedQueue(blockingQueue);
}
}
4.编写方法
1.Service
public interface DelayQueue {
public Boolean offer(Object object);
public void offer(Object object, Long time, TimeUnit timeUnit);
public void offerAsync(Object object, Long time, TimeUnit timeUnit);
public Boolean offerAsync(Object object);
}
2.Service实现类
@Component
public class RedissionDelayQueue implements DelayQueue{
@Resource(name = "rDelayedQueue")
private RDelayedQueue<Object> rDelayedQueue;
@Override
public Boolean offer(Object object) {
return rDelayedQueue.offer(object);
}
@Override
public void offer(Object object, Long time, TimeUnit timeUnit) {
rDelayedQueue.offer(object, time, timeUnit);
}
@Override
public void offerAsync(Object object, Long time, TimeUnit timeUnit) {
rDelayedQueue.offerAsync(object, time, timeUnit);
}
@Override
public Boolean offerAsync(Object object) {
boolean flag = false;
RFuture<Boolean> rFuture = rDelayedQueue.offerAsync(object);
try {
flag = rFuture.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
return flag;
}
}
5.开启一个线程,持续监听
@Resource(name = "rBlockingQueue")
private RBlockingQueue<Object> rBlockingQueue;
//延时队列,设置状态为已超时
@PostConstruct
public void take(){
new Thread(() -> {
while(true) {
try {
Object obj = rBlockingQueue.take();
System.out.println(obj);
} catch (InterruptedException e){
e.printStackTrace();
}
}
}).start();
}
6. 调用
接下来只要调用,RedissionDelayQueue 中的方法就可以实现,过期的任务会通过rBlockingQueue.take();返回
@Autowired
private DelayQueue delayQueue;
public void test(){
delayQueue.offer("test",10L, TimeUnit.SECONDS);
}
上述代码缩写,take()方法将在10S后返回过期的内容test