• Redis使用ZSET实现消息队列使用总结一


    转载请注明出处:

    1.zset为什么可以做消息队列

      zset做消息队列的特性有:

    1. 有序性:zset中所有元素都被自动排序。这让zset很适合用于有序的消息队列,因为可以根据一个或多个标准(比如消息的到达时间或优先级)按需检索消息。

    2. 元素唯一性:zset的每个元素都是独一无二的,这对于实现某些消息需求(比如幂等性)是非常有帮助的。

    3. 成员和分数之间的映射关系:有序集合中的每个成员都有一个分数,这样就可以将相同的数据划分到不同的 queue 中,以及为每个 queue 设置不同的延时。

    4. 高效的添加删除操作:因为zset会自动维护元素之间的顺序,所以在添加或删除元素时无需进行手动排序,从而能提升操作速度。

      综上所述,Redis的zset天然支持按照时间顺序的消息队列,可以利用其成员唯一性的特性来保证消息不被重复消费,在实现高吞吐率等方面也有很大的优势。

    2.zset实现消息队列的步骤

     Redis的zset有序集合是可以用来实现消息队列的,一般是按照时间戳作为score的值,将消息内容作为value存入有序集合中。

     实现步骤:

    1. 客户端将消息推送到Redis的有序集合中。

    2. 有序集合中,每个成员都有一个分数(score)。在这里,我们可以设成消息的时间戳,也就是当时的时间。

    3. 当需要从消息队列中获取消息时,客户端获取有序集合前N个元素并进行操作。一般来说,N取一个适当的数值,比如10。

      需要注意的是,Redis的zset是有序集合,它的元素是有序的,并且不能有重复元素。因此,如果需要处理有重复消息的情况,需要在消息体中加入某些唯一性标识来保证不会重复。

    3.使用jedis实现消息队列示例

     Java可以通过Redis的Java客户端包Jedis来使用Redis,Jedis提供了丰富的API来操作Redis,下面是一段实现用Redis的zset类型实现的消息队列的代码。

    复制代码
    import redis.clients.jedis.Jedis;
    import java.util.Set;
    
    public class RedisMessageQueue {
        private Jedis jedis; //Redis连接对象
        private String queueName; //队列名字
    
        /**
         * 构造函数
         * @param host Redis主机地址
         * @param port Redis端口
         * @param password Redis密码
         * @param queueName 队列名字
         */
        public RedisMessageQueue(String host, int port, String password, String queueName){
            jedis = new Jedis(host, port);
            jedis.auth(password);
            this.queueName = queueName;
        }
    
        /**
         * 发送消息
         * @param message 消息内容
         */
        public void sendMessage(String message){
            //获取当前时间戳
            long timestamp = System.currentTimeMillis();
            //将消息添加到有序集合中
            jedis.zadd(queueName, timestamp, message);
        }
    
        /**
         * 接收消息
         * @param count 一次接收的消息数量
         * @return 返回接收到的消息
         */
        public String[] receiveMessage(int count){
            //设置最大轮询时间
            long timeout = 5000;
            //获取当前时间戳
            long start = System.currentTimeMillis();
    
            while (true) {
                //获取可用的消息数量
                long size = jedis.zcount(queueName, "-inf", "+inf");
                if (size == 0) {
                    //如果无消息,休眠50ms后继续轮询
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } else {
                    //计算需要获取的消息数量count与当前可用的消息数量size的最小值
                    count = (int) Math.min(count, size);
                    //获取消息
                    Set messages = jedis.zrange(queueName, 0, count - 1);
                    String[] results = messages.toArray(new String[0]);
                    //移除已处理的消息
                    jedis.zremrangeByRank(queueName, 0, count - 1);
                    return results;
                }
    
                //检查是否超时
                if (System.currentTimeMillis() - start > timeout) {
                    return null; //超时返回空
                }
            }
        }
    
        /**
         * 销毁队列
         */
        public void destroy(){
            jedis.del(queueName);
            jedis.close();
        }
    }
    复制代码

      使用示例:

    复制代码
    public static void main(String[] args) {
        //创建消息队列
        RedisMessageQueue messageQueue = new RedisMessageQueue("localhost", 6379, "password", "my_queue");
    
        //生产者发送消息
        messageQueue.sendMessage("message1");
        messageQueue.sendMessage("message2");
    
        //消费者接收消息
        String[] messages = messageQueue.receiveMessage(10);
        System.out.println(Arrays.toString(messages)); //输出:[message1, message2]
    
        //销毁队列
        messageQueue.destroy();
    }
    复制代码

      在实际应用中,可以结合线程池或者消息监听器等方式,将消息接收过程放置于独立的线程中,以提高消息队列的处理效率。

    4.+inf与-inf

      +inf 是 Redis 中用于表示正无穷大的一种特殊值,也就是无限大。在使用 Redis 的 zset 集合时,+inf 通常用作 ZREVRANGEBYSCORE 命令的上限值,表示查找 zset 集合中最大的分数值。+inf 后面的 -inf 表示 zset 中最小的分数值。这两个值一起可以用来获取 zset 集合中的所有元素或一个特定范围内的元素。例如:

    复制代码
    # 获取 zset 集合中所有元素
    ZREVRANGE queue +inf -inf WITHSCORES
    
    # 获取 zset 集合中第1到第10个元素(分数从大到小排列)
    ZREVRANGE queue +inf -inf WITHSCORES LIMIT 0 9
    
    # 获取 zset 集合中分数在 1581095012 到当前时间之间的元素
    ZREVRANGEBYSCORE queue +inf 1581095012 WITHSCORES
    复制代码

      在这些命令中,+inf 代表了一个最大的分数值,-inf 代表了一个最小的分数值,用于确定查询的分数值范围。

    5.redis使用list与zset做消息队列有什么区别

      Redis 使用 List 和 ZSET 都可以实现消息队列,但是二者有以下不同之处:

    1. 数据结构不同:List 是一个有序的字符串列表,ZSET 则是一个有序集合,它们的底层实现机制不同。

    2. 存储方式不同:List 只能存储字符串类型的数据,而 ZSET 则可以存储带有权重的元素,即除了元素值外,还可以为每个元素指定一个分数。

    3. 功能不同: List 操作在元素添加、删除等方面比较方便,而 ZSET 在处理数据排序和范围查找等方面比 List 更加高效。

    4. 应用场景不同: 对于需要精细控制排序和分值的场景可以选用 ZSET,而对于只需要简单的队列操作,例如先进先出,可以直接采用 List。

      综上所述,List 和 ZSET 都可以用于消息队列的实现,但如果需要更好的性能和更高级的排序功能,建议使用 ZSET。而如果只需要简单的队列操作,则 List 更加适合。

  • 相关阅读:
    MySQL--MySQL索引事务
    我为什么更推荐你使用cmake编译grpc程序?
    JSP开发环境搭建(Tomcat的安装和配置)
    公众号内容拓展学习笔记(2022.7.5)
    使用Token管理用户session
    STM32的IAP讲解
    spring循环依赖-不仅仅是八股文
    基于黏菌算法优化概率神经网络PNN的分类预测 - 附代码
    Android ConstraintLayout
    PTA 睡前速刷(C++ & Java)
  • 原文地址:https://www.cnblogs.com/zjdxr-up/p/17218044.html