Redis Zset实现延时队列

前言

本篇博文意在使用Redis模拟实现延时队列.

Redis中的有序集合Zset可以实现延时队列,Zset可以看作是缩小版的redis,可以看作是用来存储键值对的集合,是集合名-K-V的结构,在Zset中,会按照Score进行排序。

有序集合中键值对的键被称为成员,值被称为分值,分值必须为浮点数。

命令行为
ZADD将一个带有给定分值的成员添加到有序集合中,返回添加元素的个数
ZRANGE根据元素在有序排列中的位置,从有序集合里面获取多个元素
ZRANGEBYSCORE根据一个分值段来获取在该分值段的所有元素
ZREMZREM key member-------如果给定成员存在于该有序集合,则删除该成员
ZCARDZCARD key--------返回有序集合包含的成员数量
ZCOUNTZCOUNT key min max----------返回分值介于min 和max之间的成员数量
ZSCOREZSOCRE key member --------返回成员的分值
ZINCRBYZINCRBY key increment member -----将member成员的分值加上increment

示例

127.0.0.1:6379> ZADD ZZ 728 member1
(integer) 1
127.0.0.1:6379> ZADD ZZ 729 member2
(integer) 1
127.0.0.1:6379> ZADD ZZ 729 member2
(integer) 0
127.0.0.1:6379> ZRANGE ZZ 0 -1
1) "member1"
2) "member2"
127.0.0.1:6379> ZRANGEBYSCORE ZZ 728 728
1) "member1"
127.0.0.1:6379> ZREM ZZ member1
(integer) 1

第三行我们在键为ZZ的有序集合中添加了已经存在的键,所以返回0标识已存在,但是会覆盖原来的值。

第四行我们通过ZRANGE命令来获取下标范围的键值对,这里只返回显示了成员,如果需要返回分值,则在命令后加withscores即可。

思路

把当前时间戳和延时时间相加,也就是到期时间,存入Redis中,然后不断轮询,找到到期的,拿到再删除即可。

实战

这里使用RedisTemplate作为Redis客户端连接,放在延时队列中的可以看成是一个个延时任务

为了适配不同类型的对象存入Zset,这里我们使用泛型

/**
 * @author 阳光大男孩!!!
 */
@Data
public class DelayTask<T> {
	// 消息id
    private String id;
    // 任务名称
    private String taskName;
    // 具体任务内容
    private T msg;

}

延时队列具体实现,主要分为放任务和轮询任务两个部分,放任务

public class RedisDelayQueue<T> {
    /**
     * 延迟队列名称
     */
    private String delayQueueName = "delayQueue";
  
    // 传入redis客户端操作
    public RedisDelayQueue(RedisTemplate redisTemplate,String delayQueueName)
    {
        this.redisTemplate = redisTemplate;
        this.delayQueueName = delayQueueName;
    }
    /**
     * 设置延迟消息
     */
    public void setDelayTasks(T msg, long delayTime) {
        DelayTask<T> delayTask = new DelayTask<>();
        delayTask.setId(UUID.randomUUID().toString());
        delayTask.setMsg(msg);
        Boolean addResult = redisTemplate.opsForZSet().add(delayQueueName, JSONObject.toJSONString(delayTask), System.currentTimeMillis() + delayTime);
        if(addResult)
        {
            System.out.println("添加任务成功!"+JSONObject.toJSONString(delayTask)+"当前时间为"+ LocalDateTime.now());
        }
    }
    /**
     * 监听延迟消息
     */
    public void listenDelayLoop() {
        while (true) {
            // 获取一个到点的消息
            Set<String> set = redisTemplate.opsForZSet().rangeByScore(delayQueueName, 0, System.currentTimeMillis(), 0, 1);

            // 如果没有,就等等
            if (set.isEmpty()) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                // 继续执行
                continue;
            }
            // 获取具体消息的key
            String it = set.iterator().next();
            // 删除成功
            if (redisTemplate.opsForZSet().remove(delayQueueName, it) > 0) {
                // 拿到任务
                DelayTask delayTask = JSONObject.parseObject(it, DelayTask.class);
                // 后续处理
                System.out.println("消息到期"+delayTask.getMsg().toString()+",时间为"+LocalDateTime.now());
            }
        }
    }
}

  • 12
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值