redis stream 消息转移原理
前言
同一消费者组的消费者,每个消费者都接受消息的子集进行处理,并在从失败中恢复时重新读取仅发送给他们的待处理消息。然而,在现实世界中,消费者可能会永久失败,永远不会恢复。消费者停止后,从未恢复的待处理消息会发生什么?
一. XPEDNING
> XPENDING mystream mygroup
1) (integer) 2
2) 1526569498055-0
3) 1526569506935-0
4) 1) 1) "Bob"
2) "2"
以这种方式调用时,该命令输出消费者组中待处理消息的总数(在本例中为两个),待处理消息中较低和较高的消息ID,最后输出消费者列表和他们拥有的待处理消息数量。我们只有Bob有两条待处理的消息,因为Alice请求的单条消息是使用XACK确认的。
我们可以通过向XPENDING提供更多参数来请求更多信息,因为完整的命令如下:
XPENDING <key> <groupname> [[IDLE <min-idle-time>] <start-id> <end-id> <count> [<consumer-name>]]
通过提供开始和结束ID(可以像XRANGE中一样是-和+)以及计数来控制命令返回的信息量,我们能够了解有关待处理消息的更多信息。如果我们想将输出限制为给定消费者的待处理消息,但不会在以下示例中使用此功能,则使用可选的最终参数,即消费者名称。
> XPENDING mystream mygroup - + 10
1) 1) 1526569498055-0
2) "Bob"
3) (integer) 74170458
4) (integer) 1
2) 1) 1526569506935-0
2) "Bob"
3) (integer) 74170458
4) (integer) 1
现在我们有了每条消息的详细信息:ID、消费者名称、以毫秒为单位的空闲时间,最后消息被传递的次数。
有了这样一个Pending机制,就意味着在某个消费者读取消息但未处理后,消息是不会丢失的。
此时还有一个问题,就是若某个消费者宕机之后,没有办法再上线了,那么就需要将该消费者Pending的消息,转移给其他的消费者处理,就是消息转移。就学要用到下面的命令 XCLAIM.
二.XCLAIM
XCLAIM <key> <group> <consumer> <min-idle-time> <ID-1> <ID-2> ... <ID-N>
基本上,我们说,对于这个特定的jke y和组,我希望指定的消息ID将更改所有权,并将分配给指定的消费者名称。然而,我们还提供了最小的空闲时间,因此只有当上述消息的空闲时间大于指定的空闲时间时,操作才会起作用。这很有用,因为也许两个客户端正在同时重试认领一条消息:
Client 1: XCLAIM mystream mygroup Alice 3600000 1526569498055-0
Client 2: XCLAIM mystream mygroup Lora 3600000 1526569498055-0
声明消息将重置其空闲时间,并将消息被传递的次数+1,因此第二个消费者将无法得到它。通过这种方式,我们避免了对消息重复处理。
执行结果是
> XCLAIM mystream mygroup Alice 3600000 1526569498055-0
1) 1) 1526569498055-0
2) 1) "message"
2) "orange"
爱丽丝成功认领了这条消息,她现在可以处理这条消息并确认它,即使原始消费者没有恢复,也可以推动事情向前发展。
从上面的示例中可以清楚地看出,作为成功声明给定消息的副作用,XCLAIM命令也会返回它。然而,这不是强制性的。可以使用JUSTID选项仅返回成功声明的消息的ID。如果您想减少客户端和服务器之间的带宽(以及命令的性能),并且您对消息不感兴趣,因为您的消费者的实现方式会不时重新扫描挂起消息的历史记录。
转移也可以通过一个单独的流程实现:一个只是检查待处理消息列表,并将空闲消息分配给似乎活跃的消费者。活跃的消费者可以使用Redis流的可观察性功能之一获得。
三.自动转移
Redis 6.2中添加的XAUTOCLAIM命令实现了我们上面描述的转移过程。XPENDING和XCLAIM为恢复机制提供了基本的构建块。此命令通过让Redis管理来优化通用进程,并为大多数恢复需求提供了简单的解决方案。
XAUTOCLAIM识别闲置的挂起消息,并将其所有权转让给消费者。命令如下所示:
XAUTOCLAIM <key> <group> <consumer> <min-idle-time> <start> [COUNT count] [JUSTID]
因此,在上面的例子中,我本可以使用自动声明来声明一条像这样的消息:
> XAUTOCLAIM mystream mygroup Alice 3600000 0-0 COUNT 1
1) 1526569498055-0 (流id,下次调用中使用它来继续读取空闲的挂起消息)
2) 1) 1526569498055-0 (挂起的消息条目)
2) 1) "message"
2) "orange"
与XCLAIM一样,该命令用消息数组进行回复,但它也返回一个流ID,允许迭代挂起的条目。流ID是一个光标,我可以在下次调用中使用它来继续读取空闲的挂起消息:
> XAUTOCLAIM mystream mygroup Lora 3600000 1526569498055-0 COUNT 1
1) 0-0
2) 1) 1526569506935-0
2) 1) "message"
2) "strawberry"
当XAUTOCLAIM返回“0-0”流ID作为光标时,这意味着它到达了消费者组待处理条目列表的末尾。这并不意味着没有新的空闲待处理消息,因此该过程通过从流开始调用XAUTOCLAIM继续进行。
4.转移和私信队列
您在XPENDING输出中看到的计数器是每条消息的交付次数。计数器以两种方式递增:当通过XCLAIM成功声明消息时,或者当使用XREADGROUP调用来访问挂起消息的历史记录时。
当出现故障时,消息会被多次发送是正常的,但最终它们通常会被处理和确认。然而,处理某些特定消息时可能会出现问题,因为它被损坏或以触发处理代码中错误的方式制作。在这种情况下,消费者将不断无法处理此特定信息。因为我们有交付尝试的计数器,我们可以使用该计数器来检测由于某种原因无法处理的消息。因此,一旦交付计数器达到您选择的给定大数量,将此类消息放在另一个流中并向系统管理员发送通知可能更明智。这基本上是Redis Streams实现死信概念的方式。
5.XINFO 查看流的信息
缺乏可观察性的消息系统很难使用。不知道谁在消费消息,哪些消息正在等待,在给定流中活跃的一组消费者群体,使一切变得不透明。出于这个原因,Redis Streams和消费者团体有不同的方式来观察正在发生的事情。我们已经涵盖了XPENDING,它允许我们检查特定时刻正在处理的消息列表,以及它们的空闲时间和交付次数。
然而,我们可能想做的更多,XINFO命令是一个可观察性接口,可以与子命令一起使用,以获取有关流或消费者组的信息。
此命令使用子命令来显示有关流及其消费者组状态的不同信息。例如,XINFO STREAM报告有关流本身的信息。
> XINFO STREAM mystream
1) "length"
2) (integer) 2
3) "radix-tree-keys"
4) (integer) 1
5) "radix-tree-nodes"
6) (integer) 2
7) "last-generated-id"
8) "1638125141232-0"
9) "max-deleted-entryid"
10) "0-0"
11) "entries-added"
12) (integer) 2
13) "groups"
14) (integer) 1
15) "first-entry"
16) 1) "1638125133432-0"
2) 1) "message"
2) "apple"
17) "last-entry"
18) 1) "1638125141232-0"
2) 1) "message"
2) "banana"
输出显示有关流如何内部编码的信息,还显示流中的第一条和最后一条消息。另一个可用的信息是与该流相关的消费者群体的数量。我们可以进一步挖掘,询问有关消费者群体的更多信息。
> XINFO GROUPS mystream
1) 1) "name"
2) "mygroup"
3) "consumers"
4) (integer) 2
5) "pending"
6) (integer) 2
7) "last-delivered-id"
8) "1638126030001-0"
9) "entries-read"
10) (integer) 2
11) "lag"
12) (integer) 0
2) 1) "name"
2) "some-other-group"
3) "consumers"
4) (integer) 1
5) "pending"
6) (integer) 0
7) "last-delivered-id"
8) "1638126028070-0"
9) "entries-read"
10) (integer) 1
11) "lag"
12) (integer) 1
正如您在本次和之前的输出中看到的,XINFO命令输出了一系列字段值项。因为它是一个可观察性命令,它允许人类用户立即了解报告的信息,并允许该命令通过添加更多字段来报告更多信息,而不会破坏与旧客户端的兼容性。其他必须具有更高带宽效率的命令,如XPENDING,只需在没有字段名称的情况下报告信息。
使用GROUPS子命令的上述示例的输出应清晰地观察字段名称。我们可以通过检查在该组中注册的消费者来更详细地检查特定消费者组的状态。
> XINFO CONSUMERS mystream mygroup
1) 1) name
2) "Alice"
3) pending
4) (integer) 1
5) idle
6) (integer) 9104628
2) 1) name
2) "Bob"
3) pending
4) (integer) 1
5) idle
6) (integer) 83841983
如果您不记得命令的语法,只需向命令本身寻求帮助:
> XINFO HELP
1) XINFO <subcommand> [<arg> [value] [opt] ...]. Subcommands are:
2) CONSUMERS <key> <groupname>
3) Show consumers of <groupname>.
4) GROUPS <key>
5) Show the stream consumer groups.
6) STREAM <key> [FULL [COUNT <count>]
7) Show information about the stream.
8) HELP
9) Prints this help.