Redis消息队列初探

3 篇文章 0 订阅
1 篇文章 0 订阅

什么是消息队列?

消息队列(Message Queue/Broker),顾名思义:存放消息的队列。是分布式系统中重要的组件,利用高效可靠的消息传递机制进行与平台解耦的数据通信。主要解决应用耦合、异步处理、流量削锋等问题。

Redis实现消息队列

  • 基于List数据结构实现消息队列
  • 基于发布订阅机制实现消息队列
  • 基于Stream数据结构实现消息队列

基于List数据结构实现消息队列

Redis的List结构基于双向链表实现,所以基于lpush和rpop或者rpush和lpop指令即可,同时支持阻塞获取指令BRPOP或BLPOP

127.0.0.1:6379> LPUSH queue 1 2 3 4
(integer) 4
127.0.0.1:6379> Rpop queue
"1"
127.0.0.1:6379> Rpop queue
"2"
127.0.0.1:6379> Rpop queue
"3"
127.0.0.1:6379> Rpop queue
"4"
127.0.0.1:6379> Rpop queue
(nil)

由于列队的特性是:先进先出,所以只要从list结构一端进,另一端出即可。上述demo中时使用LPUSH和RPOP实现简单队列的效果, 当队列中不存在元素时,返回nil。

若要支持阻塞获取队列元素时,需要结合LPUSH和BRPOP命令使用。

使用BRPOP命令会阻塞当前客户端,直到队列中存在元素或等待时间达到。
在这里插入图片描述

127.0.0.1:6379> LPUSH queue 15 16
(integer) 2

127.0.0.1:6379> BRPOP queue 100
1) "queue" --队列名称
2) "15"   --消息体
(5.16s) --获取消息等待的时间

优点:

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

缺点

  • 不能保证消息被消费(无法避免消息丢失)
  • 不能重复消费,一旦消费就被删除(无法被多个消费者 消费同一条消息)

基于发布订阅机制实现消息队列

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

  • 一个channel可以有一个或多个订阅者
  • 一个发布者可向多个channel发送消息

PUBLISH 发布

PUBLISH channel message
summary: Post a message to a channel
retuen: the number of clients that received the message.

PUBLISH channel(通道name)message(消息体)
返回:接受该消息的客户端数量,包括匹配模式。

SUBSCRIBE 订阅

SUBSCRIBE channel [channel …]
summary: Listen for messages published to the given channels

127.0.0.1:6379> SUBSCRIBE channel1
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "channel1"
3) (integer) 1
...阻塞等待channel中的消息...

当channel收到消息时:

127.0.0.1:6379> SUBSCRIBE channel1
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "channel1"
3) (integer) 1
1) "message"
2) "channel1"
3) "k1=v1"

PSUBSCRIBE 模式匹配订阅

PSUBSCRIBE pattern [pattern …]
summary: Listen for messages published to channels matching the given patterns

支持的模式匹配规则:

  • h?llo subscribes to hello, hallo and hxllo

  • h*llo subscribes to hllo and heeeello

  • h[ae]llo subscribes to hello and hallo, but not hillo

    '?' :匹配单个字符 '*' :匹配多个字符 '[]':匹配自定义[]内部字符

    127.0.0.1:6379> PSUBSCRIBE chan*
    Reading messages... (press Ctrl-C to quit)
    1) "psubscribe"
    2) "chan*"
    3) (integer) 1
    1) "pmessage"
    2) "chan*"   --返回匹配的规则
    3) "channel1"--返回匹配规则的通道name
    4) "k1=v1"   --返回消息体
    

优点:

  • 采用发布订阅模型,支持多生产、多消费
  • 阻塞读取消息(但无法设置阻塞时间)

缺点:

  • 不支持数据持久化
  • 无法保证数据被消费(无法避免消息丢失)
  • 消息堆积有上限,超出限制将丢失旧的数据

基于Stream数据结构实现消息队列

stream单消费者模式

xlen:查询消息长度

XLEN key
summary: Return the number of entries in a stream

xlen 队列名称

xadd :添加消息

XADD key [NOMKSTREAM] [MAXLEN|MINID [=|~] threshold [LIMIT count]] *|ID field value [field value …]

  • key(队列名称);

  • [NOMKSTREAM](若指定,当队列不存在则不创建。默认创建);

  • [MAXLEN|MINID [=|~] threshold [LIMIT count]] 设置消息队列的最大消息数量,若超过数量,但未被消费,则会抛弃旧的数据;

  • *|ID 表示自动生成唯一key,是unix毫秒时间戳-同一毫秒的序列号。也可以自定义自增id。

  • field value [field value …] 消息key1 消息value1 消息key2 消息value2 …消息体键值对
    在这里插入图片描述

    DEMO:

    127.0.0.1:6379> xadd stream1 * k1 v1 k2 v2
    "1692755653814-0" -- *,代表redis自增ID,由时间戳-递增数字
    
xread:读取消息

XREAD [COUNT count] [BLOCK milliseconds] STREAMS key [key …] ID [ID …]]

  • count n 可选,读取n条数据

  • block 0 可选,阻塞等待毫秒数。block设置为0时表示没有消息就一直阻塞,不设置block就是非阻塞模式,拿不拿到都返回,拿不到返回nil

  • streams key [key …] 必填,指定一个或多个队列名称
    在这里插入图片描述

    • 设置ID [ID …]为指定消息ID,将**(阻塞)** 获取该消息ID之后的消息
    • 设置ID [ID …] 为$,将**(阻塞)** 获取队列最新消息
    • 设置ID [ID …] 为0,将**(阻塞)** 获取队列第一个消息及之后的消息

    DEMO:

    127.0.0.1:6379> xread count 1 block 1000 streams stream1 $
    (nil) 	 --阻塞时间达到,若channel没有消息则返回nil
    (1.02s)  --返回阻塞等待的时间
    
    127.0.0.1:6379> xread count 1 block 10000 streams stream1 $
    1) 1) "stream1"                 --队列名称
       2) 1) 1) "1692755905302-0"   --消息ID
             2) 1) "k1"             --消息体
                2) "v1"
                3) "k2"
                4) "v2"
    (3.20s)
    
    

优点:

  • 采用stream机数据结构,支持多生产、多消费
  • 支持数据持久化(消息可回溯)
  • 消息可阻塞读取(可设置阻塞时间)

缺点:

  • 无法保证数据被消费(无法避免消息丢失)——未标记消息已处理,也没有ack确认机制,无法保证消息最少被消费一次。

stream消费者组模式

消费者组 (Consumer Group): 将多个消费者划分到一个组中,监听同一个队列。

具体以下特点:

  • 消息分流
    • 队列中的消息会分流给组内的不同消费者而不是重复消费,从而加快消息处理的速度
  • 消息标识
    • 消费者组会维护一个标识记录最后一个被处理的消息,哪怕消费者宕机重启,还会从标示之后读取消息。确保每一个消息都会被消费
  • 消息确认
    • 消费者获取消息后,消息处于pending状态,并存入一个pending-list。当消息处理完成后==需要通过XACK来确认消息==,标记消息为已处理,才会从pendingList移除
xgroup create:创建消费者组

XGROUP CREATE key groupname ID|$ [MKSTREAM]]

xgroup create 队列名称 消费组名 起始消费id(0-0 或0表示从头开始消费,$表示从尾即最新消息开始消费)

  • key: 队列名称
  • groupName: 消费者组名称
  • ID: 起始ID标示,$代表队列中最后一个消息,0则代表队列中第一个消息
  • MKSTREAM: 队列不存在时自动创建队列

还存在其他命令,如下:
在这里插入图片描述

DEMO:

127.0.0.1:6379> XGROUP create stream2 group1 $ mkstream
OK
分组读取消息:xreadgroup

XREADGROUP GROUP group consumer [COUNT count] [BLOCK milliseconds] [NOACK] STREAMS key [key …] ID [ID …]

xreadgroup group 消费组名 消费者 (count n 可选参数,读取n条数据)(block 0 可选参数,阻塞等待毫秒数) streams 队列名称 消息id

  • group: 消费组名称

  • consumer: 消费者名称,如果消费者不存在,会自动创建一个消费者

  • count: 本次查询的最大数量

  • BLOCK milliseconds: 当没有消息时最长等待时间

  • NOACK: 无需手动ACK,获取到消息后自动确认**(不建议使用)**

  • STREAMS key: 指定队列名称

  • ID: 获取消息的起始ID: ——读取过的不再重复读,

    • 设置具体消息id读取大于指定id的消息(不包含指定id的消息)
    • '>' :=从下一个未消费的消息开始(读取最新消息的意思)=
    • 其它:根据指定id从pending-list中获取已消费但未确认的消息,例如0,是从pending-list中的第一个消息开始(因为每次消费pending-list中的消息+确认ACK后,就会将该条消息从pending-list移除,所以0每次都是队列中刷新后的第一天消息)
  • xreadgroup是写命令,只能在主服务器进行(它修改了游标等信息)。

DEMO:
在这里插入图片描述
可以看出,在消费者组内接收消息,不会重复消费,每次都是获取的下一条未处理的消息。

若设置ID 为其他时,如,设置ID=0。
在这里插入图片描述
两次获取pending-list中的同一条数据,因为未确认该消息,该消息一直在pending-list的第一条。我们将这条消息确认后可得到:
在这里插入图片描述
确认该条消息,再次执行XREADGROUP group group1 c1 count 1 block 10000 streams stream2 0 ,可见已经获取的是第二条数据k2=v2了:
在这里插入图片描述

未确认消息:XPENDING

XPENDING key group [[IDLE min-idle-time] start end count [consumer]]
summary: Return information and entries from a stream consumer group pending entries list, that are messages fetched but never acknowledged.

  • key: 队列名称
  • group: 消费组名称
  • [IDLE min-idle-time] : 消息被消费开始,到确认XACK之前的时间
  • start : 需要获取未确认消息的起始下标-:代表负无穷
  • end : 需要获取未确认消息的末尾下标+:代表正无穷
  • count: 获取未确认消息的数量
  • consumer: 指定消费者名称**(因为每个消费者消费过的消息,都存在自己的pending-list中)**

DEMO:

127.0.0.1:6379> XPENDING stream2 group1 - + 100
1) 1) "1692758490032-0"           --消费ID
   2) "c1"                        --消费者名称
   3) (integer) 815246
   4) (integer) 1
2) 1) "1692758493333-0"
   2) "c1"
   3) (integer) 811406
   4) (integer) 1
3) 1) "1692758496586-0"
   2) "c2"
   3) (integer) 784393
   4) (integer) 1
4) 1) "1692758499886-0"
   2) "c2"
   3) (integer) 782980
   4) (integer) 1
5) 1) "1692758503581-0"
   2) "c1"
   3) (integer) 779702
   4) (integer) 1
6) 1) "1692758508874-0"
   2) "c2"
   3) (integer) 776924
   4) (integer) 1

可看出上述C1、C2消费者各自存在3条待确认的消息。

确认消息:xack

XACK key group ID [ID …]

xack 队列名称 消费组名 消息id…

已经读取但是未确认的消息会写入pending_list,确认XACK后会从pending_list删除,是为了确保消息至少被消费一次。
在这里插入图片描述

优点:

  • 采用stream机数据结构,支持多生产、多消费
  • 支持数据持久化(消息可回溯)
  • 消息可阻塞读取(可设置阻塞时间)
  • 没有消息漏读的风险
  • 支持消息确认机制,保证消息最少被消费一次

Redis队列总结:

笔者在此仅以List、PubSub、Stream三种类型的消息队列进行对比,数据如下:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值