延时队列是一种特殊的队列,它允许将消息或任务延迟一段时间后再进行处理。延时队列的作用是在某些需要延迟处理的场景中,提供一种可靠的延迟处理机制。
延时队列的原理可以通过以下步骤来解释:
技术没有最好的只有最合适的。
延时队列在项目中的应用还是比较多的,尤其像电商类平台:
对于数据量比较少并且时效性要求不那么高的场景,一种比较简单的方式是轮询数据库,定期轮询一下数据库中所有数据,处理所有到期的数据。比如公司内部会议预定系统,因为数据量必然不会很大并且会议开始前提前 30 分钟提醒与提前 29 分钟提醒的差别并不大。
但是数据量比较大实时性要求比较高,尤其电商平台每天的所有新建订单 15 分钟内未支付的自动超时,数量级高达百万甚至千万,这时候轮询数据库则不可取。这种场景下,就需要使用延迟队列提供了一种高效的处理大量需要延迟消费消息的解决方案。
DelayQueue
是Java并发包java.util.concurrent
中提供的一个支持延时获取元素的无界阻塞队列。它的内部实现是基于优先级队列(PriorityQueue),按照元素的过期时间进行排序(所以元素必须是可比较), 只有过期元素才会出队。
PriorityQueue
是带有优先级的无界阻塞队列, 每次出队都返回优先级最高或者最低的元素。其内部使用的是平衡二叉树实现的,所以可以很容易的获取到优先级最高或者最低的元素(大顶堆、小顶堆)。所以元素必须是可排序的,这样才能通过比较器comparator
比较各个元素。
队列元素需要实现Delayed接口,
getDelay()方法用于设置延迟时间,
compareTo()方法用于对队列中的元素进行排序。入队:
put()、offer(),线程安全。
出队:
poll(),非阻塞方式获取,没有到期的元素直接返回null。
take(),阻塞方式获取,没有到期的元素线程将会等待。
优点:
JDK自带的,不用引入其他框架或中间件,使用简单方便。
缺点∶
不支持分布式或持久化,重启后会丢失,如果订单并发量非常大,因为DelayQueve是无界的,订单量越大,队列内的对象就越多,可能造成0OM的风险。所以使用DelayQueue实现延时任务,只适用于任务量较小的情况。
流程简介
原理:
KeyExpirationEventMessageListener
就是一个订阅了Redis过期事件的客户端。它使用Redis的PSUBSCRIBE
命令来订阅一个特殊的频道(channel),通常是__keyevent@0__:expired
,其中0
表示所有数据库,expired
表示过期事件。__keyevent@0__:expired
频道的客户端,其中就包括了KeyExpirationEventMessageListener
。导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
配置redis连接
spring:
redis:
host: 150.158.34.233
port: 6379
password: lxy123456
lettuce:
pool:
max-active: 15
min-idle: 0
max-idle: 8
max-wait: 100
timeout: 1000
开启redis过期键服务
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.listener.KeyExpirationEventMessageListener;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.stereotype.Service;
/**
* Description: Redis超时监听器
* User: liw
* Date: 2023-05-20
*/
@Service
public class RedisKeyExpirationListener extends KeyExpirationEventMessageListener {
private static Logger log = LoggerFactory.getLogger(RedisKeyExpirationListener.class);
public RedisKeyExpirationListener(RedisMessageListenerContainer listenerContainer) {
super(listenerContainer);
}
@Override
public void onMessage(Message message, byte[] pattern) {
//获取过期的key
String expireKey = message.toString();
log.debug("失效+key is:"+ expireKey);
System.out.println(expireKey);
//这里还可以根据key的自定义前缀来判断执行哪个条件
//......
}
}
缺点:
使用Redis的key过期事件来处理延迟任务是一种常见的方法,但它也有一些缺点和限制:
虽然Redis的过期事件可以用于一些简单的延迟任务场景,但对于更复杂、大规模、高可用性和高可靠性的延迟任务处理,通常需要考虑使用专门的延迟队列或任务调度系统。这些系统提供了更强大的功能,例如精确的延迟控制、重试机制、监控和管理工具,以及高可用性的保证。选择适合你需求的解决方案是根据具体情况而定。
Zset,即有序集合(Sorted Set),是 Redis 提供的一种复杂数据类型。Zset 是 set 的升级版,它在 set 的基础上增加了一个权重参数 score,使得集合中的元素能够按 score 进行有序排列。
在 Zset 中,集合元素的添加、删除和查找的时间复杂度都是 O(1)。这得益于 Redis 使用的是一种叫做跳跃列表(skiplist)的数据结构来实现 Zset。
Zset 的主要特性包括:
在 Redis 中,ZADD
命令用于向有序集合(Zset)中添加一个或多个成员,或者更新已存在成员的分数。它的基本语法如下:
ZADD key score member [score member ...]
1
其中,key
是有序集合的名称,score
是成员的分数,member
是成员的值。你可以一次添加一个或多个成员。
例如,你可以使用以下命令向名为 myzset
的有序集合中添加一个成员 one
,其分数为 1
:
ZADD myzset 1 one
1
如果你想要一次添加多个成员,可以在命令后面依次列出它们的分数和值,例如:
ZADD myzset 1 one 2 two 3 three
1
这个命令会向 myzset
集合中添加三个成员,它们的分数分别为 1
、2
和 3
。
如果添加的成员在有序集合中已经存在,那么它的分数会被更新为新的值,同时该成员在集合中的位置也会相应地发生变化。
在 Redis 中,ZSCORE
命令用于返回有序集合(Zset)中,指定成员的分数。它的基本语法如下:
ZSCORE key member
1
其中,key
是有序集合的名称,member
是要查询分数的成员。
例如,你可以使用以下命令查询名为 myzset
的有序集合中,成员 one
的分数:
ZSCORE myzset one
1
如果指定的成员存在于有序集合中,那么命令会返回该成员的分数。如果指定的成员不存在于有序集合中,那么命令会返回 nil
。
需要注意的是,ZSCORE
命令返回的分数是字符串形式的浮点数。
在 Redis 中,ZRANK
命令用于返回有序集合(Zset)中指定成员的排名,其中分数值从低到高排序。它的基本语法如下:
ZRANK key member
1
其中,key
是有序集合的名称,member
是要查询排名的成员。
例如,你可以使用以下命令查询名为 myzset
的有序集合中,成员 one
的排名
ZRANK myzset one
1
如果指定的成员存在于有序集合中,那么命令会返回该成员的排名。排名以 0 为底,也就是说,分数最低的成员排名为 0。
如果指定的成员不存在于有序集合中,那么命令会返回 nil
。
需要注意的是,ZRANK
命令返回的排名是字符串形式的整数。
ZREVRANK key member:返回有序集合中指定成员的索引,分数值从高到低排序。
ZRANGE key start stop [WITHSCORES]:返回有序集中,指定区间内的成员。
ZREVRANGE key start stop [WITHSCORES]:返回有序集中,指定区间内的成员,通过索引,分数值从高到低。
ZREM key member [member …]:移除有序集合中的一个或多个成员。
ZCARD key:获取有序集合的成员数。
ZCOUNT key min max:计算在有序集合中指定区间分数的成员数。
ZINCRBY key increment member:为有序集合中的成员添加增量。
ZRANGEBYSCORE:返回分值介于min和max的之间的成员.
p [WITHSCORES]:返回有序集中,指定区间内的成员,通过索引,分数值从高到低。
ZREM key member [member …]:移除有序集合中的一个或多个成员。
ZCARD key:获取有序集合的成员数。
ZCOUNT key min max:计算在有序集合中指定区间分数的成员数。
ZINCRBY key increment member:为有序集合中的成员添加增量。
ZRANGEBYSCORE:返回分值介于min和max的之间的成员.