为什么设计Stream
基于Reids的消息队列实现有很多种,例如:
- PUB/SUB(订阅/发布模式)
- 但是发布订阅模式是无法持久化的,如果出现网络断开、Redis 宕机等,消息就会被丢弃。
- 基于 List LPUSH+BRPOP 或者基于 Sorted-Set 的实现
- 支持了持久化,但是不支持多播,分组消费等。
Redis5.0 中还增加了一个数据结构 Stream,从字面上看是流类型,但其实从功能上看,应该是Redis对消息队列(MQ,Message Queue)的完善实现。
他弥补了 Redis Pub/Sub 不能支持持久化消息的缺陷。
他是一个新的数据结构,他是一个可以支持多播持久化的消息队列。
从上图看,它将所有加入的消息形成了一个消息链表,每个小小都有一个唯一的ID以及其对应内容,而且消息是持久化的,所以代表了 Redis 重启之后,内容不会消失。
每个 Stream 都有唯一的名称,就是 Redis 的 Key,我们在首次使用 xadd
指令追加消息的时候会自动创建。
每个 Stream 都可以挂多个消费组,每个消费组会有个游标 last_delivered_id 在 Stream 数组之上往前移动,表示当前消费组已经消费到哪条消息了。
- 消费组:
每个消费组都有一个 Stream 内唯一的名称,消费组不会自动创建,它需要单独的指令 xgroup create
进行创建,需要指定 从 Stream 的某个消息 ID 开始消费,这个 ID 用来初始化 last_delivered_id 变量。
每个消费组不会互相影响,状态独立,换句话说就是 Stream 内部消息会被每个消费组都消费到。
- 消费者:
然后同一个消费组中有多个消费者,每个消费者有一个组内唯一ID,消费者之间是竞争关系,任意一个消费者读取了消息之后,last_delivered_id 都会往后移动。
消费者内部有个状态变量 pending_ids ,它记录了当前已经被客户端读取的消息但是没有确认(ack)。如果此时客户端没有 确认(ack),那么在这个变量里的消息 ID 会越来越多,一旦某个消息被确认,他才开始减少。
这个 pending_ids 变量官方称之为 PEL(Pending Entries List),一个很核心的数据结构,确保客户端至少消费了一次消息,而不会在网络传输中把没处理过的消息丢失。
消息ID
消息 ID 的形式它是 timestampInMillis-sequence,比如 123132132-21,他表示当前的消息在毫秒时间戳 123132132 产生,并且是该毫秒内的第 21 条消息。
它可以是由服务器自动生成也可是客户端自己指定,但是必须是单调递增。
消息内容
消息内容就是键值对,如 hash 结构的键值对。
基本操作
操作 | 说明 |
---|---|
xadd | 追加消息 |
xdel | 删除消息,这里删除时逻辑删除,不影响消息总长度 |
xrange | 获取消息列表,会自动过滤已经删除的消息 |
xlen | 消息长度 |
del | 删除Stream |
xrevrange | 反向获取消息列表,ID从大到小 |
xread | 以阻塞或非阻塞方式获取消息列表 |
# * 号表示服务器自动生成 ID,后面顺序跟着一堆 key/value
# 名字叫 laoqian,年龄 30 岁
127.0.0.1:6379> xadd codehole * name laoqian age 30
1527849609889-0 # 生成的消息 ID
127.0.0.1:6379> xadd codehole * name xiaoyu age 29
1527849629172-0
127.0.0.1:6379> xadd codehole * name xiaoqian age 1
1527849637634-0
127.0.0.1:6379> xlen codehole
(integer) 3
# - 表示最小值 , + 表示最大值
127.0.0.1:6379> xrange codehole - +
127.0.0.1:6379> xrange codehole - +
1) 1) 1527849609889-0
2) 1) "name"
2) "laoqian"
3) "age"
4) "30"
2) 1) 1527849629172-0
2) 1) "name"
2) "xiaoyu"
3) "age"
4) "29"
3) 1) 1527849637634-0
2) 1) "name"
2) "xiaoqian"
3) "age"
4) "1"
# 指定最大消息 ID 的列表
127.0.0.1:6379> xrange codehole - 1527849629172-0
1) 1) 1527849609889-0
2) 1) "name"
2) "laoqian"
3) "age"
4) "30"
2) 1) 1527849629172-0
2) 1) "name"
2)<