# BLPOP
**BLPOP key [key ...] timeout**
[BLPOP](#blpop) 是列表的阻塞式(blocking)弹出原语。
它是 [_LPOP_](lpop.html#lpop) 命令的阻塞版本,当给定列表内没有任何元素可供弹出的时候,连接将被 [BLPOP](#blpop) 命令阻塞,直到等待超时或发现可弹出元素为止。
当给定多个 `key` 参数时,按参数 `key` 的先后顺序依次检查各个列表,弹出第一个非空列表的头元素。
**非阻塞行为**
当 [BLPOP](#blpop) 被调用时,如果给定 `key` 内至少有一个非空列表,那么弹出遇到的第一个非空列表的头元素,并和被弹出元素所属的列表的名字一起,组成结果返回给调用者。
当存在多个给定 `key` 时, [BLPOP](#blpop) 按给定 `key` 参数排列的先后顺序,依次检查各个列表。
假设现在有 `job` 、 `command` 和 `request` 三个列表,其中 `job` 不存在, `command` 和 `request` 都持有非空列表。考虑以下命令:
`BLPOP job command request 0`
[BLPOP](#blpop) 保证返回的元素来自 `command` ,因为它是按”查找 `job` -> 查找 `command` -> 查找 `request` “这样的顺序,第一个找到的非空列表。
```
redis> DEL job command request # 确保key都被删除
(integer) 0
redis> LPUSH command "update system..." # 为command列表增加一个值
(integer) 1
redis> LPUSH request "visit page" # 为request列表增加一个值
(integer) 1
redis> BLPOP job command request 0 # job 列表为空,被跳过,紧接着 command 列表的第一个元素被弹出。
1) "command" # 弹出元素所属的列表
2) "update system..." # 弹出元素所属的值
```
**阻塞行为**
如果所有给定 `key` 都不存在或包含空列表,那么 [BLPOP](#blpop) 命令将阻塞连接,直到等待超时,或有另一个客户端对给定 `key` 的任意一个执行 [_LPUSH_](lpush.html#lpush) 或 [_RPUSH_](rpush.html#rpush) 命令为止。
超时参数 `timeout` 接受一个以秒为单位的数字作为值。超时参数设为 `0` 表示阻塞时间可以无限期延长(block indefinitely) 。
```
redis> EXISTS job # 确保两个 key 都不存在
(integer) 0
redis> EXISTS command
(integer) 0
redis> BLPOP job command 300 # 因为key一开始不存在,所以操作会被阻塞,直到另一客户端对 job 或者 command 列表进行 PUSH 操作。
1) "job" # 这里被 push 的是 job
2) "do my home work" # 被弹出的值
(26.26s) # 等待的秒数
redis> BLPOP job command 5 # 等待超时的情况
(nil)
(5.66s) # 等待的秒数
```
**相同的key被多个客户端同时阻塞**
相同的 `key` 可以被多个客户端同时阻塞。
不同的客户端被放进一个队列中,按『先阻塞先服务』(first-BLPOP,first-served)的顺序为 `key` 执行 [BLPOP](#blpop) 命令。
**在MULTI/EXEC事务中的BLPOP**
[BLPOP](#blpop) 可以用于流水线(pipline,批量地发送多个命令并读入多个回复),但把它用在 [_MULTI_](../transaction/multi.html#multi) / [_EXEC_](../transaction/exec.html#exec) 块当中没有意义。因为这要求整个服务器被阻塞以保证块执行时的原子性,该行为阻止了其他客户端执行 [_LPUSH_](lpush.html#lpush) 或 [_RPUSH_](rpush.html#rpush) 命令。
因此,一个被包裹在 [_MULTI_](../transaction/multi.html#multi) / [_EXEC_](../transaction/exec.html#exec) 块内的 [BLPOP](#blpop) 命令,行为表现得就像 [_LPOP_](lpop.html#lpop) 一样,对空列表返回 `nil` ,对非空列表弹出列表元素,不进行任何阻塞操作。
```
# 对非空列表进行操作
redis> RPUSH job programming
(integer) 1
redis> MULTI
OK
redis> BLPOP job 30
QUEUED
redis> EXEC # 不阻塞,立即返回
1) 1) "job"
2) "programming"
# 对空列表进行操作
redis> LLEN job # 空列表
(integer) 0
redis> MULTI
OK
redis> BLPOP job 30
QUEUED
redis> EXEC # 不阻塞,立即返回
1) (nil)
```
**可用版本:**
>= 2.0.0
**时间复杂度:**
O(1)
**返回值:**
如果列表为空,返回一个 `nil` 。否则,返回一个含有两个元素的列表,第一个元素是被弹出元素所属的 `key` ,第二个元素是被弹出元素的值。
## 模式:事件提醒
有时候,为了等待一个新元素到达数据中,需要使用轮询的方式对数据进行探查。
另一种更好的方式是,使用系统提供的阻塞原语,在新元素到达时立即进行处理,而新元素还没到达时,就一直阻塞住,避免轮询占用资源。
对于 Redis ,我们似乎需要一个阻塞版的 [_SPOP_](../set/spop.html#spop) 命令,但实际上,使用 [BLPOP](#blpop) 或者 [_BRPOP_](brpop.html#brpop) 就能很好地解决这个问题。
使用元素的客户端(消费者)可以执行类似以下的代码:
```
LOOP forever
WHILE SPOP(key) returns elements
... process elements ...
END
BRPOP helper_key
END
```
添加元素的客户端(消费者)则执行以下代码:
```
MULTI
SADD key element
LPUSH helper_key x
EXEC
```
- Redis 文档
- 键空间通知(keyspace notification)
- 事务(transaction)
- 发布与订阅(pub/sub)
- 复制(Replication)
- 通信协议(protocol)
- 持久化(persistence)
- Sentinel
- 集群教程
- Redis 集群规范
- Redis 命令参考
- Key(键)
- DEL
- DUMP
- EXISTS
- EXPIRE
- EXPIREAT
- KEYS
- MIGRATE
- MOVE
- OBJECT
- PERSIST
- PEXPIRE
- PEXPIREAT
- PTTL
- RANDOMKEY
- RENAME
- RENAMENX
- RESTORE
- SORT
- TYPE
- SCAN
- String(字符串)
- APPEND
- BITCOUNT
- BITOP
- DECR
- DECRBY
- GET
- GETBIT
- GETRANGE
- GETSET
- INCR
- INCRBY
- INCRBYFLOAT
- MGET
- MSET
- MSETNX
- PSETEX
- SET
- SETBIT
- SETEX
- SETNX
- SETRANGE
- STRLEN
- Hash(哈希表)
- HDEL
- HEXISTS
- HGET
- HGETALL
- HINCRBY
- HINCRBYFLOAT
- HKEYS
- HLEN
- HMGET
- HMSET
- HSET
- HSETNX
- HVALS
- HSCAN
- List(列表)
- BLPOP
- BRPOP
- BRPOPLPUSH
- LINDEX
- LINSERT
- LLEN
- LPOP
- LPUSH
- LRANGE
- LREM
- LSET
- LTRIM
- RPOP
- RPOPLPUSH
- RPUSH
- RPUSHX
- Set(集合)
- SADD
- SCARD
- SDIFF
- SDIFFSTORE
- SINTER
- SINTER
- SINTERSTORE
- SISMEMBER
- SMEMBERS
- SMOVE
- SPOP
- SRANDMEMBER
- SREM
- SUNION
- SUNIONSTORE
- SSCAN
- SortedSet(有序集合)
- ZADD
- ZCARD
- ZCOUNT
- ZINCRBY
- ZRANGE
- ZRANGEBYSCORE
- ZRANK
- ZREM
- ZREMRANGEBYRANK
- ZREMRANGEBYSCORE
- ZREVRANGE
- ZREVRANGEBYSCORE
- ZREVRANK
- ZSCORE
- ZUNIONSTORE
- ZINTERSTORE
- ZSCAN
- Pub/Sub(发布/订阅)
- PSUBSCRIBE
- PUBLISH
- PUBSUB
- PUNSUBSCRIBE
- SUBSCRIBE
- UNSUBSCRIBE
- Transaction(事务)
- DISCARD
- EXEC
- MULTI
- UNWATCH
- WATCH
- Script(脚本)
- EVAL
- EVALSHA
- SCRIPT EXISTS
- SCRIPT FLUSH
- SCRIPT KILL
- SCRIPT LOAD
- Connection(连接)
- AUTH
- ECHO
- PING
- QUIT
- SELECT
- Server(服务器)
- BGREWRITEAOF
- BGSAVE
- CLIENT GETNAME
- CLIENT KILL
- CLIENT LIST
- CLIENT SETNAME
- CONFIG GET
- CONFIG RESETSTAT
- CONFIG REWRITE
- CONFIG SET
- DBSIZE
- DEBUG OBJECT
- DEBUG SEGFAULT
- FLUSHALL
- FLUSHDB
- INFO
- LASTSAVE
- MONITOR
- PSYNC
- SAVE
- SHUTDOWN
- SLAVEOF
- SLOWLOG
- SYNC
- TIME
- 关于