基于Stream的Redis消息队列

Posted 哪 吒

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于Stream的Redis消息队列相关的知识,希望对你有一定的参考价值。

目录

一、消息队列

消息队列(Message Queue),就是存放消息的队列。

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

  1. 消息队列:存储和管理消息,也被称为消息代理(Message Broker);
  2. 生产者:发送消息到消息队列;
  3. 消费者:从消息队列获取消息并处理消息;

Redis提供了三种不同的方式来实现消息队列:

  1. list结构:基于List结构模拟消息队列;
  2. PubSub:基本的点对点消息模型;
  3. Stream:比较完善的消息队列模型;

二、基于List结构模拟消息队列

LPUSH 结合 RPOP实现队列的入口,RPUSH 结合 LPOP实现队列的出口,当队列中没有消息时RPOP或LPOP操作会返回null。并不像JVM的阻塞队列那样会阻塞并等待消息。因此这里应该使用BRPOP或者BLPOP来实现阻塞效果。

基于List的消息队列的优点:

  1. 利用Redis存储,不受限于JVM内存上限;
  2. 基于Redis的持久化机制,数据安全性有保证;
  3. 可以满足消息有序性;

基于List的消息队列的缺点:

  1. 无法避免消息丢失;
  2. 只支持单消费;

三、基于PubSub的消息队列

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

  • SUBSCRIBE channel [channel] :订阅一个或多个频道;
  • PUBLISH channel msg :向一个频道发送消息;
  • PSUBSCRIBE pattern[pattern] :订阅与pattern格式匹配的所有频道;

基于PubSub的消息队列的优点:

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

基于PubSub的消息队列的缺点:

  1. 不支持数据持久化;
  2. 无法避免消息丢失;
  3. 消息堆积有上限,超出时数据丢失;

四、基于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 ...]

  1. count:每次读取消息的最大数量;
  2. BLOCK:当没有消息时,是否阻塞,阻塞时长,如果是0,则表示一直阻塞;
  3. STREAMS:要从哪个队列读取消息,key就是队列名;
  4. ID:起始id,只返回大于该id的消息(0表示从第一个消息开始,$代表从最新的消息开始);

最简单的xadd语句(阻塞读取):XREAD COUNT 1 BLOCK 0 STREAMS mystream $

3、STREAM类型消息队列的XREAD命令特点:

  1. 消息可回溯;
  2. 一个消息可以被多个消费者读取;
  3. 可以阻塞读取;
  4. 有消息漏读的风险

五、基于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 ...]

  1. group:消费组名称
  2. consumer:消费者名称,如果消费者不存在,会自动创建一个消费者
  3. count:本次查询的最大数量
  4. BLOCK milliseconds:当没有消息时最长等待时间
  5. NOACK:无需手动ACK,获取到消息后自动确认,自动ACK可能会出现消息丢失,所以一般需要手动ACK
  6. STREAMS key:指定队列名称
  7. ID:获取消息的起始ID:“>”:从下一个未消费的消息开;其它:根据指定id从pending-list中获取已消费但未确认的消息,例如0,是从pending-list中的第一个消息开始,消费完并确认后,则会从pending-list中移除,正常情况通过">"读取消息,出现异常情况后,再从pending-list中读取。

七、STREAM类型消息队列的XREADGROUP命令特点

  1. 消息可回溯
  2. 可以多消费者争抢消息,加快消费速度
  3. 可以阻塞读取
  4. 没有消息漏读的风险
  5. 有消息确认机制,保证消息至少被消费一次

八、三种方式对比

ListPubSubStream
消息持久化支持不支持支持
阻塞读取支持支持支持
消息堆积处理受限于内存空间,可以利用多消费者加快处理受限于消费者缓冲区受限于队列长度,可以利用消费者组提高消费速度,减少堆积
消息确认机制不支持不支持支持
消息回溯不支持不支持支持



NoSQL数据库进阶实战

NoSQL数据库进阶实战1,那些年学过的NoSQL基础

NoSQL数据库进阶实战2,NoSQL数据存储模式

Redis缓存穿透、击穿、雪崩到底是个啥?7张图告诉你

Redis分布式锁的实现方式

Redis分布式缓存、秒杀

哪吒精品系列文章

Java学习路线总结,搬砖工逆袭Java架构师

10万字208道Java经典面试题总结(附答案)

Java基础教程系列

Java高并发编程系列

数据库进阶实战系列

以上是关于基于Stream的Redis消息队列的主要内容,如果未能解决你的问题,请参考以下文章

Redis基于Stream的消息队列 - 消费者组模式

Redis进阶学习04---秒杀优化和消息队列

redis 属于redis的 “消息队列”:redis stream(浅析)

redis 属于redis的 “消息队列”:redis stream(浅析)

redis源码学习redis 中的“消息队列” Stream

redis源码学习redis 中的“消息队列” Stream