基础

Stream结构相当于一个消息队列。消息是存储在磁盘上的,而且是链式结构。每个stream都有自己的唯名称,是redis的key。stream在集群情况下,也是异步复制的。

每个stream上可以有多个消费组,每个消费组都可以有自己的游标last_delivered_id在Stream上往前移动,表示当前消费组消费到了那条消息。

消费组有Stream内的唯一的名称,使用xgroup_create创建消费组并指定名称,创建的时候,需要指定某个消息的ID,该ID用于初始化last_delivered_id变量。

同一个消费组的各个Consumer是竞争关系,只要有一个效消费了消息,那么就会移动last_derivered_id

消费者内部有一个状态变量pending_ids ,记录了当前已经被客户端读取但是还没有ack的消息。如果当前客户端没有ack,则其内部的消息ID越来越多,一旦某个消息被ack,则变量减少。该变量成为PEL (pending ertries list),该变量用来保证客户端至少消费了一次,并且不会在网络中途丢失了而没被处理。PEL内部存储的是已经发出去的消息的ID。客户端在重连redis之后,可以再次收到PEL中的ID列表。xreadgroup的起始消息必须是任意有效的消息ID,一般参数设置为0-0,表示读取所有PEL的消息以及last_delivered_id之后的新消息。

如果消息一直没有ack,则会导致PEL过大,消耗内存。

消息的ID是timeStampInMillis-sqquence,表示当前毫秒的消息序列;消息的内容是键值对,形如Hash结构的键值对。

redis stream消息队列java redis stream消息队列pending_链表

基本操作方式

操作Stream

  • xadd:向Stream追加消息
  • xdel:从Stream中删除消息,删除仅仅是设置标志位,不影响消息总长度。
  • xrange:获取Stream中的消息列表,自动过滤已经删除的消息。-表示最小值,+表示最大值。
  • xlen:获取Stream的消息长度,所有在链表中存在的消息
  • del:删除整个Stream中的所有消息。

xadd命令,可以指定最长的长度,干掉旧的消息:

xadd myStream maxlen 1000  aaa bb ccc # 新增aaa bb ccc消息,同时链表不超过1000长度

独立消费:使用xread指令,把Stream当作普通的链表使用,此时我们可以忽略消费组的存在。注意,使用xread指令,一定要记得当前消费到的ID,下次调用xread时,需要传入该ID作为参数。可以进行阻塞操作:

xread block 1000 count 1 streams myStream $

myStream队尾读取1个消息,不存在时阻塞1s

创建消费组
使用xgroup create创建效费组,需要提供其实消息的ID来初始化last_delivered_id变量。
比如:

xgroup create myStream cg1 0-0  # 从头部开始消费
xgroup create myStream cg2 $ # 从尾部开始消费

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uONW3ZH0-1574302168977)(en-resource://database/2763:1)]

消费消息
客户端使用xreadgroup命令对组内消息进行消费,提供:消费组名称、消费者名称和启示消息ID,可以阻塞等待新的消息。消费者读取消息后,对一个的消息ID进入PEL中,消费者消费完毕后,需要发送XACK,此时PEL中的结构会删除对应的消息。注意,消费完消息后,一定要执行XACK !!!!
比如:

xreadgroup GROUP cg1 c1 count 1 streams myStream
xreadgroup GROUP cg1 c1 block 0 count 1 streams myStream

Redis的Stream不支持分区,需要使用多个Stream才可以执行分区。