目录
- 一、消息队列
- 二、基于List结构模拟消息队列
- 基于List的消息队列的优点:
- 基于List的消息队列的缺点:
- 三、基于PubSub的消息队列
- 基于PubSub的消息队列的优点:
- 基于PubSub的消息队列的缺点:
- 四、基于Stream的消息队列
- 1、XADD语法
- 2、XREAD 语法
- 3、STREAM类型消息队列的XREAD命令特点:
- 五、基于Stream的消息队列-消费者组
- 1、消息分流
- 2、消息标示
- 3、消息确认
- 六、消费者组常见命令
- 1、创建消费者组
- 2、删除指定的消费者组
- 3、给指定的消费者组添加消费者
- 4、删除消费者组中的指定消费者
- 5、从消费者组读取消息
- 七、STREAM类型消息队列的XREADGROUP命令特点
- 八、三种方式对比
- NoSQL数据库进阶实战
- 哪吒精品系列文章
一、消息队列
消息队列(Message Queue),就是存放消息的队列。
最简单的消息队列模型包括3个角色:
- 消息队列:存储和管理消息,也被称为消息代理(Message Broker);
- 生产者:发送消息到消息队列;
- 消费者:从消息队列获取消息并处理消息;
Redis提供了三种不同的方式来实现消息队列:
- list结构:基于List结构模拟消息队列;
- PubSub:基本的点对点消息模型;
- Stream:比较完善的消息队列模型;
二、基于List结构模拟消息队列
LPUSH 结合 RPOP实现队列的入口,RPUSH 结合 LPOP实现队列的出口,当队列中没有消息时RPOP或LPOP操作会返回null。并不像JVM的阻塞队列那样会阻塞并等待消息。因此这里应该使用BRPOP或者BLPOP来实现阻塞效果。
基于List的消息队列的优点:
- 利用Redis存储,不受限于JVM内存上限;
- 基于Redis的持久化机制,数据安全性有保证;
- 可以满足消息有序性;
基于List的消息队列的缺点:
- 无法避免消息丢失;
- 只支持单消费;
三、基于PubSub的消息队列
PubSub(发布订阅)是Redis2.0版本引入的消息传递模型。顾名思义,消费者可以订阅一个或多个channel,生产者向对应channel发送消息后,所有订阅者都能收到相关消息。
- SUBSCRIBE channel [channel] :订阅一个或多个频道;
- PUBLISH channel msg :向一个频道发送消息;
- PSUBSCRIBE pattern[pattern] :订阅与pattern格式匹配的所有频道;
基于PubSub的消息队列的优点:
- 采用发布订阅模型,支持多生产、多消费
基于PubSub的消息队列的缺点:
- 不支持数据持久化;
- 无法避免消息丢失;
- 消息堆积有上限,超出时数据丢失;
四、基于Stream的消息队列
Stream 是 Redis 5.0 引入的一种新数据类型,可以实现一个功能非常完善的消息队列。
1、XADD语法
XADD 命令将指定的流条目追加到指定 key 的流中。如果 key 不存在,将使用流的条目自动创建 key。
XADD key ID field value[field value ...]
ID 标识流内的给定条目。如果指定的 ID 参数是字符*(星号 ASCII 字符),XADD 命令会自动为您生成一个唯一的 ID。但是,也可以指定一个良好格式的 ID,以便新的条目以指定的 ID 准确存储,虽然仅在极少数情况下有用。
ID 是由-隔开的两个数字组成的:1798432684128-1。两个部分数字都是 64 位的,当自动生成 ID 时,第一部分是生成 ID 的 Redis 实例的毫秒格式的 Unix 时间。第二部分只是一个序列号,以及是用来区分同一毫秒内生成的 ID 的。
ID 保证始终是递增的:如果比较刚插入的条目的 ID,它将大于其他任何过去的 ID,因此条目在流中是完全排序的。为了保证这个特性,如果流中当前最大的 ID 的时间大于实例的当前本地时间,将会使用前者,并将 ID 的序列部分递增。例如,本地始终回调了,或者在故障转移之后新主机具有不同的绝对时间,则可能发生这种情况。
当用户为 XADD 命令指定显式 ID 时,最小有效的 ID 是0-1,并且用户必须指定一个比当前流中的任何 ID 都要大的 ID,否则命令将失败。通常使用特定 ID 仅在您有另一个系统生成唯一 ID(例如 SQL 表),并且您确实希望 Redis 流 ID 与该另一个系统的 ID 匹配时才有用。
最简单的xadd语句:XADD mystream * name nezha age 18
2、XREAD 语法
XREAD [COUNT count] [BLOCK < milliseconds >] STREAMS key [key ...] ID [ID ...]
- count:每次读取消息的最大数量;
- BLOCK:当没有消息时,是否阻塞,阻塞时长,如果是0,则表示一直阻塞;
- STREAMS:要从哪个队列读取消息,key就是队列名;
- ID:起始id,只返回大于该id的消息(0表示从第一个消息开始,$代表从最新的消息开始);
最简单的xadd语句(阻塞读取):XREAD COUNT 1 BLOCK 0 STREAMS mystream $
3、STREAM类型消息队列的XREAD命令特点:
- 消息可回溯;
- 一个消息可以被多个消费者读取;
- 可以阻塞读取;
- 有消息漏读的风险
五、基于Stream的消息队列-消费者组
消费者组(Consumer Group):将多个消费者划分到一个组中,监听同一个队列。具备下列特点:
1、消息分流
队列中的消息会分流给组内的不同消费者,而不是重复消费,从而加快消息处理的速度。
2、消息标示
消费者组会维护一个标示,记录最后一个被处理的消息,哪怕消费者宕机重启,还会从标示之后读取消息。确保每一个消息都会被消费。
3、消息确认
费者获取消息后,消息处于pending状态,并存入一个pending-list。当处理完成后需要通过XACK来确认消息,标记消息为已处理,才会从pending-list移除。
六、消费者组常见命令
1、创建消费者组
XGROUP CRATE key groupName ID [MKSTREAM]
- key:队列名称;
- groupName:消费者组名称;
- ID:起始id标识,$代表队列中最后一个消息,0代表队列中的第一个消息;
- MKSTREAM:队列不存在时,自动创建队列;
2、删除指定的消费者组
XGROUP DESTORY key groupName
3、给指定的消费者组添加消费者
XGROUP CREATECONSUMER key groupName consumername
。
4、删除消费者组中的指定消费者
XGROUP DELCONSUMER key groupName consumername
。
5、从消费者组读取消息
XREADGROUP GROUP group consumer [COUNT count] [BLOCK milliseconds] [NOACK] STREAMS key [key ...] ID [ID ...]
。
- group:消费组名称
- consumer:消费者名称,如果消费者不存在,会自动创建一个消费者
- count:本次查询的最大数量
- BLOCK milliseconds:当没有消息时最长等待时间
- NOACK:无需手动ACK,获取到消息后自动确认,自动ACK可能会出现消息丢失,所以一般需要手动ACK
- STREAMS key:指定队列名称
- ID:获取消息的起始ID:“>”:从下一个未消费的消息开;其它:根据指定id从pending-list中获取已消费但未确认的消息,例如0,是从pending-list中的第一个消息开始,消费完并确认后,则会从pending-list中移除,正常情况通过">"读取消息,出现异常情况后,再从pending-list中读取。
七、STREAM类型消息队列的XREADGROUP命令特点
- 消息可回溯
- 可以多消费者争抢消息,加快消费速度
- 可以阻塞读取
- 没有消息漏读的风险
- 有消息确认机制,保证消息至少被消费一次
八、三种方式对比
List | PubSub | Stream | |
---|---|---|---|
消息持久化 | 支持 | 不支持 | 支持 |
阻塞读取 | 支持 | 支持 | 支持 |
消息堆积处理 | 受限于内存空间,可以利用多消费者加快处理 | 受限于消费者缓冲区 | 受限于队列长度,可以利用消费者组提高消费速度,减少堆积 |
消息确认机制 | 不支持 | 不支持 | 支持 |
消息回溯 | 不支持 | 不支持 | 支持 |
NoSQL数据库进阶实战
NoSQL数据库进阶实战1,那些年学过的NoSQL基础
NoSQL数据库进阶实战2,NoSQL数据存储模式
Redis缓存穿透、击穿、雪崩到底是个啥?7张图告诉你
Redis分布式锁的实现方式
Redis分布式缓存、秒杀
哪吒精品系列文章
Java学习路线总结,搬砖工逆袭Java架构师
10万字208道Java经典面试题总结(附答案)
Java基础教程系列
Java高并发编程系列
数据库进阶实战系列