BLPOP:阻塞式左端弹出操作
BLPOP 命令是带有阻塞功能的左端弹出操作,它接受任意多个列表以及一个秒级精度的超时时限作为参数:
BLPOP list [list ...] timeout
BLPOP 命令会按照从左到右的顺序依次检查用户给定的列表,并对最先遇到的非空列表执行左端元素弹出操作。如果 BLPOP 命令在检查了用户给定的所有列表之后都没有发现可以执行弹出操作的非空列表,那么它将阻塞执行该命令的客户端并开始等待,直到某个给定列表变为非空,又或者等待时间超出给定时限为止。
当 BLPOP 命令成功对某个非空列表执行了弹出操作之后,它将向用户返回一个包含两个元素的数组:数组的第一个元素记录了执行弹出操作的列表,即被弹出元素的来源列表,而数组的第二个元素则是被弹出元素本身。
比如在以下这个 BLPOP 命令执行示例中,被弹出的元素 "a" 就来源于列表 alphabets:
redis> BLPOP alphabets 5 -- 尝试弹出alphabets列表的最左端元素,最多阻塞5s
1) "alphabets" -- 被弹出元素的来源列表
2) "a" -- 被弹出元素
如果用户使用的是 redis-cli 客户端,并且在执行 BLPOP 命令的过程中曾经被阻塞过,那么客户端还会将被阻塞的时长也打印出来:
redis> BLPOP message-queue 5
1) "message-queue"
2) "hello world!"
(1.60s) -- 客户端执行这个命令时被阻塞了1.6s
注意,这里展示的阻塞时长只是 redis-cli 客户端为了方便用户而添加的额外信息,BLPOP 命令返回的结果本身并不包含这一信息。
解除阻塞状态
正如前面所说,当 BLPOP 命令发现用户给定的所有列表都为空时,就会让执行命令的客户端进入阻塞状态。如果在客户端被阻塞的过程中,有另一个客户端向导致阻塞的列表推入了新的元素,那么该列表就会变为非空,而被阻塞的客户端也会随着 BLPOP 命令成功弹出列表元素而重新回到非阻塞状态。
作为例子,表4-1展示了一个客户端从被阻塞到解除阻塞的整个过程。


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

最后,如果被推入列表的元素数量少于被阻塞的客户端数量,那么先被阻塞的客户端将会先解除阻塞,而未能解除阻塞的客户端则需要继续等待下次推入操作。
比如,如果有 5 个客户端因为列表为空而被阻塞,但是推入列表的元素只有 3 个,那么最先被阻塞的 3 个客户端将会解除阻塞状态,而剩下的 2 个客户端则会继续阻塞。
处理空列表
如果用户向 BLPOP 命令传入的所有列表都是空列表,并且这些列表在给定的时限之内一直没有变成非空列表,那么 BLPOP 命令将在给定时限到达之后向客户端返回一个空值,表示没有任何元素被弹出:
redis> BLPOP empty-list 5
(nil)
(5.04s)