美文网首页一些收藏
redis消息队列的四种实现方式之List的简单队列和延时队列

redis消息队列的四种实现方式之List的简单队列和延时队列

作者: 山巅自相见 | 来源:发表于2022-01-04 18:15 被阅读0次

    目录

    redis为我们提供的数据结构是很多的,如果再Redis中要实现一个消息中间件至少有4种方式:

    1. List
    2. zSet
    3. PUB/SUB(发布与订阅)
    4. Steam

    基于List实现

    最简单是基于List来实现,如果说你的业务需求足够的简单,同时又想享有消息中间件的一部分功能,有没有实力来部署一个消息中间件的东西,那么就可以使用List。

    List提供了从头或者尾去放入元素,也提供了从头或者尾去拿取元素,而且操作时间都非常的快,那么我们就可以把List视为一个队列,我从左边往里放元素,再从右边拿元素,很完美的符合消息队列的一个简单的模型。 Redis-List数据结构
    生产者从左边去放元素,消费者就从右边拿。但是消费者什么时候拿有时候是不确定的,当不确定什么时候拿的时候,就得不停的去队列中看有没有该拿的元素。这种情况会发生CPU空转,浪费资源。

    代码详解:

    import com.zhao.utils.UuidUtil;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.redis.core.ListOperations;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    /**
     * 生产者
     */
    @RestController
    @RequestMapping("/testString")
    public class ProducerController {
      @Autowired
      private RedisTemplate redisTemplate;
    
      @PostMapping(value = "testMethodList")
      public void producerList() {
        // 创建String类型实例
        ListOperations listOperations = redisTemplate.opsForList();
        // 从左边添加数据
        listOperations.leftPush("testListOne", UuidUtil.getUuid());
        System.out.println("添加数据:" + listOperations.range("testListOne", 0, -1));
      }
    }
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.redis.core.ListOperations;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.util.ObjectUtils;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    /**
     * 消费者
     */
    @RestController
    @RequestMapping(value = "/consumerController")
    public class ConsumerController {
      @Autowired
      private RedisTemplate redisTemplate;
    
      @PostMapping(value = "consumerList")
      public void consumerList() {
        // 创建List类型实例
        ListOperations listOperations = redisTemplate.opsForList();
        // 查询已有的数据
        System.out.println(listOperations.range("testListOne", 0, -1));
        while (true) {
          System.out.println("正在拿数据");
          // 从右边拿数据
          Object testListOne = listOperations.rightPop("testListOne");
          // 如果没有拿到数据 则重新拿 直到拿到数据再停止
          if (!ObjectUtils.isEmpty(testListOne)) {
            System.out.println("拿到数据:" + testListOne.toString());
            break;
          }
        }
      }
    }
    
    import com.zhao.RedistestApplication;
    import com.zhao.controller.consumer.ConsumerController;
    import com.zhao.controller.producer.ProducerController;
    import org.junit.jupiter.api.Test;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    
    @SpringBootTest(classes = RedistestApplication.class)
    public class TestController {
      @Autowired
      private ProducerController producerController;
    
      @Autowired
      private ConsumerController consumerController;
          
      /**
       * 生产者
       */
      @Test
      public void testProducerList() {
        producerController.producerList();
      }
    
      /**
       * 消费者
       */
      @Test
      public void testConsumerList() {
        consumerController.consumerList();
      }
    }
    

    我们先启动消费者,当生产者没有往队列中放数据的时候,队列中现在还是空的,什么都没有。此时消费者就会一直不停地去访问队列,直到拿到数据为止。


    这样就形成了“数据库空转”。当启动了生产者,生产者往队列中放入一条数据,消费者拿到数据,循环停止。
    Brpop命令移出并获取列表的最后一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。 基本语法:
    redis 127.0.0.1:6379> BLPOP LIST1 LIST2 .. LISTN TIMEOUT
    

    如果列表为空,返回一个 nil 。 否则,返回一个含有两个元素的列表,第一个元素是被弹出元素所属的 key ,第二个元素是被弹出元素的值。
    java 中的实例:

    /**
     * 2
     * 延时拿数据
     */
    @PostMapping(value = "consumerList")
    public void consumerList() {
      // 创建Jedis实例
      Jedis jedis = new Jedis();
      System.out.println("开始拿数据了 testListOne现有数据:" + jedis.lrange("testListOne", 0, -1));
      // 从右边阻塞式拿数据 超时时间为0代表不限时
      List<String> testListOne = jedis.brpop(0, "testListOne");
      System.out.println("拿到数据:" + testListOne);
    }
    

    在以上实例中,操作会被阻塞,如果“testListOne”列表有数据,就会从列表的右边拿取第一个数据并返回,否则会无限期等待(可能是无限期,我自己就等了1分钟);如果超时时间大于0,会阻塞设置的时间;如果超时时间小于0,启动项目会报错。
    同理,Blpop只是从左边拿数据,就不多解释了。

    相关文章

      网友评论

        本文标题:redis消息队列的四种实现方式之List的简单队列和延时队列

        本文链接:https://www.haomeiwen.com/subject/jkspqrtx.html