redis 实现的MQ 3

37 阅读4分钟

pub/sub最大优势是:同一份消息会被推送给所有sub 问题:丢消息

  • 缺乏ack:sub在获取到消息后,无ack,因此若处理失败,想要执行消息的重放是不行的
  • 缺乏消息存储能力:消息来了就走不停留在channel,当sub宕机,redis宕机,sub消息积压(sub消费能力弱会导致缓冲区buffer积压,redis很可能会自动把sub踢下线,buffer容量可以在redis.conf配置 clinet-output-buffer-limit pubsub 32mb 8mb 60,表示某个sub的buffer大小达到32MB,sub被踢,弱buffer在连续60s达到8MB,也会被踢)

成熟的方案:redis streams

生产消息:

XADD my_streams_topic * key1 val1
"1638515664470-0"
XADD topic1 * key2 val2
"1638515672769-0"
  • my_streams_topic:topic 名称
  •  *:消息自动生成唯一标识 id,基于时间戳+自增序号生成
  • key1/val1、key2/val2:消息数据 kv 对

消费消息:

XREAD STREAMS my_streams_topic 0-0
11"my_streams_topic"
   211"1638515664470-0"
         21"key1"
            2"val1"
      21"1638515672769-0"
         21"key2"
            2"val2"
  •  my_streams_topic: topic 名称
  •  0-0:从头开始消费. 倘若这里填为某条消息 id,则代表从这条消息之后(不包含这条消息)开始消费

阻塞模式消费消息:streams 支持在消费时,采用阻塞模式进行消费. 倘若存在数据则即时返回处理,否则会阻塞消费流程.

# BLOCK 0 表示阻塞等待时没有超时时间上限
XREAD BLOCK 0 STREAMS my_streams_topic 1638515672769-0
(nil)
  • • BLOCK:阻塞消费模式
  • • 0:阻塞等待超时时间,超过这个时长会返回 nil. 设置为 0 则表示不设置超时阈值

创建消费者组

XGROUP CREATE my_streams_topic my_group 0-0
OK
  • my_streams_topic:topic 名称
  • my_group:消费者组名称
  •  0-0:从头开始消费

 基于消费者组消费消息

同一份数据在同一个消费者组下只会被消费到一次. 不同消费者组各自能获取到独立完整的消息数据.

XREADGROUP GROUP my_group consumer BLOCK 0 STREAMS my_streams_topic >
11) "topic1"
   211) "1638515664470-0"
         21) "key1"
            2) "val1"
      21) "1638515672769-0"
         21) "key2"
            2) "val2"

还有另一种消费模式,读取的是已分配给当前消费者,但是还未经确认的老消息:

XREADGROUP GROUP my_group consumer STREAMS my_streams_topic 0-0
11) "topic1"
   211) "1638515664470-0"
         21) "key1"
            2) "val1"
      21) "1638515672769-0"
         21) "key2"
            2) "val2"
XREADGROUP GROUP my_group consumer STREAMS my_streams_topic 0-0
11) "topic1"
   211) "1638515664470-0"
         21) "key1"
            2) "val1"
      21) "1638515672769-0"
         21) "key2"
            2) "val2"

• 0-0:标识读取已分配给当前 consumer ,但是还没经过 xack 指令确认的消息

确认消息:

通过 xack 指令,携带上消费者组、topic 名称以及消息 id,能够完成对某条消息的确认操作.

127.0.0.1:6379> XACK my_streams_topic my_group 1638515664470-0
(integer) 1
  • my_streams_topic:topic 名称
  • my_group:消费者组名称
  • 1638515664470-0:消息 id

基于redis-streams的mq方案分析

  • 支持pub/sub
  • 数据可持久化:streams和string,list一样,能通过rdb(redis database) aof(append only file)的落盘存储,防止降低数据丢失
  • 支持ack:consumer在处理好某条消息后,通过xack指令对消息进行确认。这也对于没经过ack确认的消息,redis streams为consumer保留了重新消费的能力
  • 支持消息缓存:streams会时机开辟内存空间来存储数据,哪怕某个consumer group在开始后才注册,也能消息溯源,从topic开始执行消息的消费操作。(这种基于内存的消息存储在资源使用上存在很大的压力和很高的成本,严重发生OOM,因此可用显式谁都那个一个topic可缓存的数据长度来人为限制这个缓存空间的容量:XADD tpoic MAXLEN 10000 * key1 val1)