BLPOP:阻塞式左端弹出操作

BLPOP 命令是带有阻塞功能的左端弹出操作,它接受任意多个列表以及一个秒级精度的超时时限作为参数:

  1. BLPOP list [list ...] timeout

BLPOP 命令会按照从左到右的顺序依次检查用户给定的列表,并对最先遇到的非空列表执行左端元素弹出操作。如果 BLPOP 命令在检查了用户给定的所有列表之后都没有发现可以执行弹出操作的非空列表,那么它将阻塞执行该命令的客户端并开始等待,直到某个给定列表变为非空,又或者等待时间超出给定时限为止。

BLPOP 命令成功对某个非空列表执行了弹出操作之后,它将向用户返回一个包含两个元素的数组:数组的第一个元素记录了执行弹出操作的列表,也即是被弹出元素的来源列表;而数组的第二个元素则是被弹出元素本身。

比如在以下这个 BLPOP 命令执行示例里面,被弹出的元素 "a" 就来源于列表 alphabets

  1. redis> BLPOP alphabets 5 -- 尝试弹出 alphabets 列表的最左端元素,最多阻塞 5
  2. 1) "alphabets" -- 被弹出元素的来源列表
  3. 2) "a" -- 被弹出元素

如果用户使用的是 redis-cli 客户端,并且在执行 BLPOP 命令的过程中曾经被阻塞过,那么客户端还会将被阻塞的时长也打印出来:

  1. redis> BLPOP message-queue 5
  2. 1) "message-queue"
  3. 2) "hello world!"
  4. (1.60s) -- 客户端执行这个命令时被阻塞了 1.6

注意,这里展示的阻塞时长只是 redis-cli 客户端为了方便用户而添加的额外信息,BLPOP 命令返回的结果本身并不包含这一信息。

解除阻塞状态

正如前面所说,当 BLPOP 命令发现用户给定的所有列表都为空时,它就会让执行命令的客户端进入阻塞状态。如果在客户端被阻塞的过程中,有另一个客户端向导致阻塞的列表推入了新的元素,那么该列表就会变为非空,而被阻塞的客户端也会随着 BLPOP 命令成功弹出列表元素而重新回到非阻塞状态。

作为例子,表 4-1 展示了一个客户端从被阻塞到解除阻塞的整个过程。


表 4-1 客户端 A 从被阻塞到解除阻塞的整个过程

时间客户端 A客户端 B
T1执行 BLPOP lst 10 ,因为 lst 为空而导致客户端被阻塞。
T2执行 RPUSH lst "hello" 命令,将 "hello" 元素推入到列表 lst 中。
T3服务器检测到导致这个客户端阻塞的lst 列表已经非空,于是从列表中弹出 "hello" 元素并将其返回给客户端。
T4接收到 "hello" 元素的客户端重新回到非阻塞状态。

如果在同一时间,有多个客户端因为同一个列表而被阻塞,那么当导致阻塞的列表变为非空时,服务器将按照“先阻塞先服务”的规则,依次为被阻塞的各个客户端弹出列表元素。

比如表 4-2 就展示了一个服务器按照先阻塞先服务规则处理被阻塞客户端的例子:在这个例子中,A、B、C 三个客户端先后执行了 BLPOP lst 10 命令,并且都因为 lst 列表为空而被阻塞,如果在这些客户端被阻塞期间,客户端 D 执行了 RPUSH lst "hello" "world" "again" 命令,那么服务器首先会处理客户端 A 的 BLPOP 命令,并将被弹出的 "hello" 元素返回给它;接着处理客户端 B 的 BLPOP 命令,并将被弹出的 "world" 元素返回给它;最后处理客户端 C 的 BLPOP 命令,并将被弹出的 "again" 元素返回给它。


表 4-2 先阻塞先服务器处理示例

时间客户端 A客户端 B客户端 C客户端 D
T1执行 BLPOP lst 10
T2执行 BLPOP lst 10
T3执行 BLPOP lst 10
T4执行 RPUSH lst "hello" "world" "again"
T5lst 列表弹出"hello"元素并解除阻塞状态。
T6lst 列表弹出"world"元素并解除阻塞状态。
T7lst 列表弹出"again"元素并解除阻塞状态。

最后,如果被推入到列表的元素数量少于被阻塞的客户端数量,那么先被阻塞的客户端将会先解除阻塞,而未能解除阻塞的客户端则需要继续等待下次推入操作。

比如说,如果有五个客户端因为列表为空而被阻塞,但是推入到列表的元素只有三个,那么最先被阻塞的三个客户端将会解除阻塞状态,而剩下的两个客户端则会继续阻塞。

处理空列表

如果用户向 BLPOP 命令传入的所有列表都是空列表,并且这些列表在给定的时限之内一直没有变成非空列表,那么 BLPOP 命令将在给定时限到达之后向客户端返回一个空值,表示没有任何元素被弹出:

  1. redis> BLPOP empty-list 5
  2. (nil)
  3. (5.04s)

列表名的作用

BLPOP 命令之所以会返回被弹出元素的来源列表,是为了让用户在传入多个列表的情况下,知道被弹出的元素来源于哪个列表。

比如在以下这个示例里面,通过 BLPOP 命令的回复,我们可以知道被弹出的元素来自于列表 queue2 ,而不是 queue1 或者 queue3

  1. redis> BLPOP queue1 queue2 queue3 5
  2. 1) "queue2"
  3. 2) "hello world!"

阻塞效果的范围

BLPOP 命令的阻塞效果只对执行这个命令的客户端有效,其他客户端以及 Redis 服务器本身并不会因为这个命令而被阻塞。

其他信息

属性
复杂度O(N) ,其中 N 为用户给定的列表数量。
版本要求BLPOP 命令从 Redis 2.0.0 版本开始可用。