1.常用命令
功能 | 命令 | 参数表 | 返回信息 |
---|---|---|---|
添加消息 | xadd | key ID field string [field string …] | entryId |
读取消息 | xread | [COUNT count] [BLOCK milliseconds] STREAMS key [key …] ID [ID …] | entryId |
读取消息 | xrange | key start end [COUNT count] | entryId |
信息监控 | xinfo | [CONSUMERS key groupname] [GROUPS key] [STREAM key] [HELP] | |
取元素数 | xlen | xlen key | Stream长度 |
删除消息 | del | key [key …] | 删除消息个数 |
结束消息 | xack | key groupname id [id…] | 受理消息数 |
创建组 | xgroup creat | [CREATE key groupname id-or- ] [ S E T I D k e y i d − o r − ] [SETID key id-or- ][SETIDkeyid−or−] [DESTROY key groupname] [DELCONSUMER key groupname consumername] | ok |
重新分配消息 | xclaim | key group consumer min-idle-time ID [ID …] [IDLE ms] [TIME ms-unix-time] [RETRYCOUNT count] [force] [justid] | 消息信息列表使用justid时为entryId列表 |
取未结束消息 | xpending | key groupname[start end count] [consumer] | group统计信息或消息明细 |
取流信息 | xinfo stream | key | 流概要信息 |
取流的组 | xinfo groups | key | 组信息列表 |
取consumer明细 | xinfo consumers | key groupname | Consumer信息列表 |
重设游标 | xgroup setid | [CREATE key groupname id-or- ] [ S E T I D k e y i d − o r − ] [SETID key id-or- ][SETIDkeyid−or−] [DESTROY key groupname] [DELCONSUMER key groupname consumername] | ok |
删除Consumer | xgroupdel consumer | key groupname consumer | 未ACK消息数 |
读取消息 | xreadgroup | group groupname consumer [count count] [block n] streams key [key…] id | > [id…] |
2.详解stream命令
1)添加消息
XADD key ID field string [field string …]
key名称自己定义,例:Queue_SQLHandle01:20191211
ID:默认*,表示由 Redis 生成消息ID,时间戳-序号:-,时间戳是毫秒级单位,是生成消息的 Redis 服务器时间,它是个64位整型(int64)。序号是在这个毫秒时间点内的消息序号,它也是个64位整型。也可以自己定义:形式必须是整数-整数,而且必须是后面加入的消息的ID要大于前面的消息ID。
field string可以有多个,类似key-value
127.0.0.1:6379> xadd ma * name liming id 1
"1575989114765-0"
通过multi批处理,添加多条内容:
127.0.0.1:6379> multi
OK
127.0.0.1:6379> xadd mq * msg 6
QUEUED
127.0.0.1:6379> xadd mq * msg 7
QUEUED
127.0.0.1:6379> xadd mq * msg 8
QUEUED
127.0.0.1:6379> exec
1) "1576034576429-0"
2) "1576034576429-1"
3) "1576034576429-2"
使用文件批量处理:
新建mq.txt,添加:
xadd mq * msg 9
xadd mq * msg 10
xadd mq * msg 11
[root@localhost redis-5.0.0]# cat mq.txt |redis-cli
"1576034912023-0"
"1576034912023-1"
"1576034912024-0"
2)读取消息
xread [COUNT count] [BLOCK milliseconds] STREAMS key [key ...] ID [ID ...]
ID:读取id比你写的大的id的消息,0取所有
127.0.0.1:6379> xread streams ma 0
1) 1) "ma"
2) 1) 1) "1575989114765-0"
2) 1) "name"
2) "liming"
3) "id"
4) "1"
count:取id比你写的大的消息,数量取前3个,这里因为没有3个比id大的消息,返回一个符合条件的。
11) 1) "1576034912023-1"
2) 1) "msg"
2) "10"
12) 1) "1576034912024-0"
2) 1) "msg"
2) "11"
127.0.0.1:6379> xread streams mq 1576034912023-1
1) 1) "mq"
2) 1) 1) "1576034912024-0"
2) 1) "msg"
2) "11"
127.0.0.1:6379> xread count 3 streams mq 1576034912023-1
1) 1) "mq"
2) 1) 1) "1576034912024-0"
2) 1) "msg"
2) "11"
- BLOCK :参数去指定允许阻塞的时间。如果不指定,表示不阻塞,立刻返回 nil。注意这一点跟 BLPOP 不同,BLPOP 一类的命令,默认是永久阻塞的。
- ID,用于设置由哪个消息ID开始读取。使用0表示从第一条消息开始。(本例中就是使用0)此处需要注意,消息队列ID是单调递增的,所以通过设置起点,可以向后读取。在阻塞模式中,可以使用$,表示最新的消息ID。(在非阻塞模式$无意义)。
- block 0:block表示命令要阻塞,0表示阻塞时间为无限大,不超时,如果设置为>0的整数,即为阻塞超时时间,单位毫秒。
- 阻塞后,拿到一条数据监听就失效,与zk的watcher雷同。要想继续拿,还要继续执行xread命令,官方推荐下一次拿entry使用上一次得到的ID。注意千万别乱设置很大的ID ,否则你可能永远拿不到entry。
127.0.0.1:6379> xread block 0 streams mq $
1) 1) "mq"
2) 1) 1) "1576039867690-0"
2) 1) "msg"
2) "14"
(26.55s)
3)读取消息
xrange key start end [COUNT count]
start end送- + 表示 ID 范围的起始位置,后者表示 ID 范围的末尾位置。
count可定义取的消息数量
127.0.0.1:6379> xrange mq - + count 2
1) 1) "1575555047885-0"
2) 1) "msg"
2) "1"
2) 1) "1575555047885-1"
2) 1) "msg"
2) "2"
7.0.0.1:6379> xrange mq 1575555047885-1 + count 3
1) 1) "1575555047885-1"
2) 1) "msg"
2) "2"
2) 1) "1575555047885-2"
2) 1) "msg"
2) "3"
3) 1) "1575555047885-3"
2) 1) "msg"
2) "3"
xrevrange key end start [COUNT count]
127.0.0.1:6379> xrevrange mq + - count 2
1) 1) "1576039867690-0"
2) 1) "msg"
2) "14"
2) 1) "1576035935677-0"
2) 1) "msg"
2) "13"
4)信息监控
xinfo [CONSUMERS key groupname] [GROUPS key] [STREAM key] [HELP]
可查看消费者,组,流的信息
127.0.0.1:6379> xinfo stream ma
1) "length"
2) (integer) 1
3) "radix-tree-keys"
4) (integer) 1
5) "radix-tree-nodes"
6) (integer) 2
7) "groups"
8) (integer) 0
9) "last-generated-id"
10) "1575989114765-0"
11) "first-entry"
12) 1) "1575989114765-0"
2) 1) "name"
2) "liming"
3) "id"
4) "1"
13) "last-entry"
14) 1) "1575989114765-0"
2) 1) "name"
2) "liming"
3) "id"
4) "1"
5)删除消息
真正删除stream
del key [key ...]
127.0.0.1:6379> del ma
(integer) 1
xdel stream_name id,删除消息并不是真正的物理删除,队列的长度不变,指示标记当前消息被删除
xdel key ID [ID ...]
2.消费者命令
1)添加消费者组
xgroup [CREATE key groupname id-or-$] [SETID key id-or-$] [DESTROY key groupname] [DELCONSUMER key groupname consumername]
在消息队列mq添加消费者组writer,最后一个参数0,表示该组从第一条消息开始消费。(意义与XREAD的0一致)。除了支持CREATE外,还支持SETID设置起始ID,DESTROY销毁组,DELCONSUMER删除组内消费者等操作。
127.0.0.1:6379> xgroup create mq writer 0
OK
2) 消费者消费消息
xreadgroup GROUP group consumer [COUNT count] [BLOCK milliseconds] STREAMS key [key ...] ID [ID ...]
消费者A消费第一条消息
xreadgroup group writer writerA count 1 streams mq >
用于组writer内消费者writerA 在队列mq中消费
参数>表示未被组内消费的起始消息
参数count 1表示获取一条
- 语法与XREAD基本一致,不过是增加了组的概念。可以进行组内消费的基本原理是,STREAM类型会为每个组记录一个最后处理(交付)的消息ID(last_delivered_id),这样在组内消费时,就可以从这个值后面开始读取,保证不重复消费。
127.0.0.1:6379> xreadgroup group writer writerA count 1 streams mq >
1) 1) "mq"
2) 1) 1) "1575555047885-0"
2) 1) "msg"
2) "1"
消费组消费时,若某个消费者,消费了某条消息,但是并没有处理成功时(例如消费者进程宕机),这条消息可能会丢失,因为组内其他消费者不能再次消费到该消息了
3) Pending 等待列表
xpending key group [start end count] [consumer]
为了解决组内消息读取但处理期间消费者崩溃带来的消息丢失问题,STREAM 设计了 Pending 列表,用于记录读取但并未处理完毕的消息。命令XPENDIING 用来获消费组或消费内消费者的未处理完毕的消息。演示如下:
127.0.0.1:6379> xpending mq writer
1) (integer) 1 # 1个已读取但未处理的消息
2) "1575555047885-0" # 起始ID
3) "1575555047885-0" # 结束ID
4) 1) 1) "writerA" # 消费者A有1个
2) "1"
再加上消费者参数,获取具体某个消费者的Pending列表
127.0.0.1:6379> xpending mq writer - + 3 writerA
1) 1) "1575555047885-0"
2) "writerA"
3) (integer) 919776
4) (integer) 1
每个Pending的消息有4个属性:消息ID所属消费者IDLE,已读取时长delivery counter,消息被读取次数上面的结果我们可以看到,我们之前读取的消息,都被记录在Pending列表中,说明全部读到的消息都没有处理,仅仅是读取了。那如何表示消费者处理完毕了消息呢?使用命令 XACK 完成告知消息处理完成
4) XACK消息处理完成
通知消息处理结束,用消息ID标识
127.0.0.1:6379> xack mq writer 1575555047885-0
(integer) 1
再次查看Pending列表
127.0.0.1:6379> xpending mq writer
1) (integer) 0
2) (nil)
3) (nil)
4) (nil)
有了这样一个Pending机制,就意味着在某个消费者读取消息但未处理后,消息是不会丢失的。等待消费者再次上线后,可以读取该Pending列表,就可以继续处理该消息了,保证消息的有序和不丢失。此时还有一个问题,就是若某个消费者宕机之后,没有办法再上线了,那么就需要将该消费者Pending的消息,转义给其他的消费者处理,就是消息转移。
4) XCLAIM消息转移
消息转移的操作时将某个消息转移到自己的Pending列表中。使用语法XCLAIM来实现,需要设置组、转移的目标消费者和消息ID,同时需要提供IDLE(已被读取时长),只有超过这个时长,才能被转移。演示如下:
消费者A读取消息2,B读取消息3、4
127.0.0.1:6379> xreadgroup group writer consumerA count 1 streams mq >
1) 1) "mq"
2) 1) 1) "1575555047885-1"
2) 1) "msg"
2) "2"
127.0.0.1:6379> xreadgroup group writer consumerB count 2 streams mq >
1) 1) "mq"
2) 1) 1) "1575555047885-2"
2) 1) "msg"
2) "3"
2) 1) "1575555047885-3"
2) 1) "msg"
2) "3"
当前属于消费者A的消息1575555047885-1,已经191,948ms未处理了
127.0.0.1:6379> xpending mq writer - + 4 consumerA
1) 1) "1575555047885-1"
2) "consumerA"
3) (integer) 191948
4) (integer) 1
查看消费者B的pending列表
127.0.0.1:6379> xpending mq writer - + 5 consumerB
1) 1) "1575555047885-2"
2) "consumerB"
3) (integer) 393838
4) (integer) 1
2) 1) "1575555047885-3"
2) "consumerB"
3) (integer) 393838
4) (integer) 1
转移超过36s的消息1575555047885-1到消费者B的Pending列表
xclaim key group consumer min-idle-time ID [ID ...] [IDLE ms] [TIME ms-unix-time] [RETRYCOUNT count] [force] [justid]
127.0.0.1:6379> xclaim mq writer consumerB 36000 1575555047885-1
1) 1) "1575555047885-1"
2) 1) "msg"
2) "2"
消息1575555047885-1已经转移到消费者B的Pending中
127.0.0.1:6379> xpending mq writer - + 5 consumerB
1) 1) "1575555047885-1"
2) "consumerB"
3) (integer) 90892 # 注意IDLE,被重置了
4) (integer) 1# 注意,读取次数也累加了1次
2) 1) "1575555047885-2"
2) "consumerB"
3) (integer) 716190
4) (integer) 1
3) 1) "1575555047885-3"
2) "consumerB"
3) (integer) 716190
4) (integer) 1
读之前
127.0.0.1:6379> xreadgroup group writer consumerA count 1 streams mq >
1) 1) "mq"
2) 1) 1) "1575555047885-4"
2) 1) "msg"
2) "4"
127.0.0.1:6379> xpending mq writer - + 6 consumerA
1) 1) "1575555047885-4"
2) "consumerA"
3) (integer) 66343
4) (integer) 1
127.0.0.1:6379> xclaim mq writer consumerB 36000 1575555047885-4
1) 1) "1575555047885-4"
2) 1) "msg"
2) "4"
127.0.0.1:6379> xpending mq writer - + 10 consumerB
1) 1) "1575555047885-1"
2) "consumerB"
3) (integer) 622668
4) (integer) 1
2) 1) "1575555047885-2"
2) "consumerB"
3) (integer) 1247966
4) (integer) 1
3) 1) "1575555047885-3"
2) "consumerB"
3) (integer) 1247966
4) (integer) 1
4) 1) "1575555047885-4"
2) "consumerB"
3) (integer) 36008
4) (integer) 1
以上代码,完成了一次消息转移。转移除了要指定ID外,还需要指定IDLE,保证是长时间未处理的才被转移。被转移的消息的IDLE会被重置,用以保证不会被重复转移,以为可能会出现将过期的消息同时转移给多个消费者的并发操作,设置了IDLE,则可以避免后面的转移不会成功,因为IDLE不满足条件。例如下面的连续两条转移,第二条不会成功。127.0.0.1:6379> XCLAIM mq mqGroup consumerB 3600000 1553585533795-1
127.0.0.1:6379> XCLAIM mq mqGroup consumerC 3600000 1553585533795-1这就是消息转移。至此我们使用了一个Pending消息的ID,所属消费者和IDLE的属性,还有一个属性就是消息被读取次数,delivery counter,该属性的作用由于统计消息被读取的次数,包括被转移也算。这个属性主要用在判定是否为错误数据上。
5) 坏消息问题,Dead Letter,死信问题
正如上面所说,如果某个消息,不能被消费者处理,也就是不能被XACK,这是要长时间处于Pending列表中,即使被反复的转移给各个消费者也是如此。此时该消息的delivery counter就会累加(上一节的例子可以看到),当累加到某个我们预设的临界值时,我们就认为是坏消息(也叫死信,DeadLetter,无法投递的消息),由于有了判定条件,我们将坏消息处理掉即可,删除即可。删除一个消息,使用XDEL语法,演示如下:# 删除队列中的消息
127.0.0.1:6379> XDEL mq 1553585533795-1
(integer) 1
查看队列中再无此消息
127.0.0.1:6379> XRANGE mq - +
1) 1) "1553585533795-0"
2) 1) "msg"
2) "1"
2) 1) "1553585533795-2"
2) 1) "msg"
2) "3"
注意本例中,并没有删除Pending中的消息因此你查看Pending,消息还会在。可以执行XACK标识其处理完毕!
6) 信息监控
XINFOStream提供了XINFO来实现对服务器信息的监控,可以查询:查看队列信息
127.0.0.1:6379> Xinfo stream mq
1) "length"
2) (integer) 7
3) "radix-tree-keys"
4) (integer) 1
5) "radix-tree-nodes"
6) (integer) 2
7) "groups"
8) (integer) 1
9) "last-generated-id"
10) "1553585533795-9"
11) "first-entry"
12) 1) "1553585533795-3"
2) 1) "msg"
2) "4"
13) "last-entry"
14) 1) "1553585533795-9"
2) 1) "msg"
2) "10"消费组信息127.0.0.1:6379> Xinfo groups mq
1) 1) "name"
2) "mqGroup"
3) "consumers"
4) (integer) 3
5) "pending"
6) (integer) 3
7) "last-delivered-id"
8) "1553585533795-4"消费者组成员信息127.0.0.1:6379> XINFO CONSUMERS mq mqGroup
1) 1) "name"
2) "consumerA"
3) "pending"
4) (integer) 1
5) "idle"
6) (integer) 18949894
2) 1) "name"
2) "consumerB"
3) "pending"
4) (integer) 1
5) "idle"
6) (integer) 3092719
3) 1) "name"
2) "consumerC"
3) "pending"
4) (integer) 1
5) "idle"
6) (integer) 23683256
至此,消息队列的操作说明大体结束!