Redis学习之消息队列

发布时间 2023-10-20 16:39:17作者: 万事胜意k

消息队列

JDK阻塞队列可能存在哪些问题?

  1. 服务器宕机,内存队列中的订单信息全部丢失

  2. 线程处理错误,已取出单个订单信息,但没有入库

  3. 受单JVM内存限制

所以,我们需要一个独立的队列来存管订单信息,也就是消息队列。

介绍

存放消息的队列。一种开发中常用的中间件

最简单的消息队列模型包括3个角色:

  • 消息队列:存储和管理消息,也被称为消息代理(Message Broker)

  • 生产者:发送消息到消息队列

  • 消费者:从消息队列获取消息并处理消息

image-20231020155417722

使用消息队列的优点

  • 可以保证我们消息的安全、不会丢失(快递柜上锁)

  • 可以解耦生产者和消费者(不用立刻去取快递)

  • 独立组件,不影响 JVM

  • 可以保证消息一定被接受,避免线程处理错误后订单丢失的问题

  • 消息是有序的

实现方式

Redis List实现

image-20231020155739261

使用 Redis List 的结构作为消息队列,使用 LPush 模拟生产者发送消息入队,使用 BRPOP(阻塞弹出)模拟消费者取出消息。没有消息时会保持阻塞状态,从而实现了类似 JVM 阻塞队列的效果。

优点:

  • 利用Redis存储,不受限于JVM内存上限

  • 基于Redis的持久化机制,数据安全性有保证

  • 可以满足消息有序性

缺点:

  • 无法避免消息丢失

  • 只支持单消费者

Redis Pub/Sub实现

PubSub(发布订阅)是Redis2.0版本引入的消息传递模型。顾名思义,消费者可以订阅一个或多个channel,生产者向对应channel发送消息后,所有订阅者都能收到相关消息。

PUBLISH channel msg :向一个频道发送消息

SUBSCRIBE channel [channel] :订阅一个或多个频道

PSUBSCRIBE pattern[pattern] :订阅与pattern格式匹配的所有频道

1653575506373

基于PubSub的消息队列有哪些优缺点? 优点:

  • 采用发布订阅模型,支持多生产、多消费

缺点:

  • 不支持数据持久化

  • 无法避免消息丢失

  • 消息堆积有上限,超出时数据丢失

Redis Stream

Stream 是 Redis 5.0 引入的一种新数据类型,可以实现一个功能非常完善的消息队列。

核心命令:

  • XAdd:添加消息 / 创建队列,消息会自动持久化、不会丢失,每个消息都有唯一 id

  • XRead:读取消息,支持多消费者读、可从指定消息 id 开始读、支持阻塞读最新消息

注:只用这两个命令还是不够的,当我们指定起始ID为$时,代表读取最新的消息,如果我们处理一条消息的过程中,又有超过1条以上的消息到达队列,则下次获取时也只能获取到最新的一条,会出现漏读消息的问题

为解决上述问题,可以用 Stream 的以下特性:

  • 消费组:同组内的多个消费者可以竞争消费(每个消息只有一个消费者能抢到),从而提高消费能力(并发度)。对应命令为 XGROUP、XREADGROUP 等。

  • 消息标识:自动记录消费的进度,支持从上次未消费的地方开始接着消费,保证每条消息按顺序消费

  • 消息确认机制:默认消费的消息为 pending 状态,会放到每个消费者的 pending list 中,只有消息由消费者确认(ACK),才会从 pending list 移除。这样如果消费业务处理异常,可以从 pending list 的开头依次读取未确认消息,重试处理。(也要避免无限重试,实在处理不成功就强制 ACK + 业务记日志)

image-20231020162344112

消费者监听消息的基本思路:

1653578211854

在Java中操作Redis Stream有两种方法:

  1. 调用lua脚本

  2. 使用Redis Template 的opsForStream()

几种方案的对比:

image-20231020162624500