系列文章目录
第一章 Nacos实现配置中心
第二章 Nacos实现注册中心
第三章 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分钟后还会再次抓取该书籍信息。
总结
这种队列模型,有什么缺点?
我们一起来分析一下:
-
不支持重复消费:消费者拉取消息后,这条消息就从 List 中删除了,无法被其它消费者再次消费,即不支持多个消费者消费同一批数据
-
消息丢失:消费者拉取到消息后,如果发生异常宕机,那这条消息就丢失了