微服务学习系列三:Redis队列

系列文章目录

第一章 Nacos实现配置中心

第二章 Nacos实现注册中心

第三章 Redis队列 


目录

系列文章目录

前言

一、Redis 列表(List)

生产者使用 LPUSH 发布消息:

二、使用步骤

1.引入库

2.阻塞式拉取消息

leftPop

rightPop

 三、 redis 队列的使用场景

总结


前言

因为Redis List 底层的实现就是一个「链表」,在头部和尾部操作元素,时间复杂度都是 O(1),这意味着它非常符合消息队列的模型。


一、Redis 列表(List)

Redis列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)

一个列表最多可以包含 232 - 1 个元素 (4294967295, 每个列表超过40亿个元素)。

生产者使用 LPUSH 发布消息:

redis 127.0.0.1:6379> lpush queue redis
1
redis 127.0.0.1:6379> lpush queue mysql
2
redis 127.0.0.1:6379> lpush queue java
3
redis 127.0.0.1:6379> lpush queue nginx
4
redis 127.0.0.1:6379> LRANGE queue 0 2
1)  "nginx"
2)  "java"
3)  "mysql"

消费者这一侧,使用 RPOP 拉取消息:

redis 127.0.0.1:6379> RPOP queue
redis
redis 127.0.0.1:6379> RPOP queue
mysql
redis 127.0.0.1:6379> RPOP queue
java

这个模型非常简单,也很容易理解。 

二、使用步骤

1.使用rightPop

而我们在编写消费者逻辑时,一般是一个「死循环」,这个逻辑需要不断地从队列中拉取消息进行处理

 while (true) {
            try {
                String message = this.stringRedisTemplate.opsForList().rightPop(key);
                log.info("onMessage.message:{}", message);

                if (StringUtils.isBlank(message)) {
                    continue;
                }


                handler.handler(message);
            } catch (Exception ex) {
                log.error("onMessage error !", ex);
            }
        }

如果此时队列为空,那消费者依旧会频繁拉取消息,这会造成「CPU 空转」,不仅浪费 CPU 资源,还会对 Redis 造成压力。

怎么解决这个问题呢?

2.阻塞式拉取消息

如果队列为空,消费者在拉取消息时就「阻塞等待」,一旦有新消息过来,就通知我的消费者立即处理新消息呢?

Redis 提供了「阻塞式」拉取消息的命令:BRPOP / BLPOP,这里的 B 指的是阻塞(Block)

leftPop

有两个重载的方法:

V leftPop(K key);

V leftPop(K key, long timeout, TimeUnit unit);

此方法会弹出list头部的元素(弹出后就在list中删除),跟栈一样,重载的那个方法,如果list不存在数据,则会阻塞住,等有数据了就会返回(最长阻塞时间就是设置的超时时间)。该方法对应到redis的操作是BLPOP(blocking left pop)

rightPop

与leftPop类似,只是从list尾部弹出数据

代码如下(示例):

 private void onMessage() {
        while (true) {
            try {
                /**
                 * 此方法会弹出list头部的元素(弹出后就在list中删除),跟栈一样,重载的那个方法,如果list不存在数据,则会阻塞住,
                 * 等有数据了就会返回(最长阻塞时间就是设置的超时时间)。
                 * 该方法对应到redis的操作是BLPOP(blocking left pop)
                 */
                String message = stringRedisTemplate.opsForList().rightPop(spiderCacheEnum.getKey(), 10, TimeUnit.SECONDS);
                log.info("onMessage.message:{}", message);

                if (StringUtils.isBlank(message)) {
                    continue;
                }


                handler.handler(message);
            } catch (Exception ex) {
                log.error("onMessage error !", ex);
            }
        }
    }

 三、 redis 队列的使用场景

  • 机票的航线抓取,近实时的数据抓取[机票的剩余票数]
起始地 目的地 cron
北京 上海 0 */3 * * * ?
北京 武汉 0 */5 * * * ?
上海 武汉 0 */10 * * * ?

      

  • 火车票的抓取,近实时的数据抓取[车票的剩余票数],可以使用定时任务,比如10分钟抓取一次,如果抓取失败,不会影响业务逻辑,后续还会再次抓取改车次数据。
  • 书籍章节内容抓取,采用定时任务抓取,生产者每10分钟生产书籍抓取任务,消费者抓取书籍信息,消费失败,不会影响业务逻辑,因为10分钟后还会再次抓取该书籍信息。

总结

这种队列模型,有什么缺点?

我们一起来分析一下:

  1. 不支持重复消费:消费者拉取消息后,这条消息就从 List 中删除了,无法被其它消费者再次消费,即不支持多个消费者消费同一批数据

  2. 消息丢失:消费者拉取到消息后,如果发生异常宕机,那这条消息就丢失了

猜你喜欢

转载自blog.csdn.net/yangyanping20108/article/details/129150009
今日推荐