前言
在 Redis 的 列表(list) 命令中,有一些命令是阻塞模式的,比如:BRPOP, BLPOP, BRPOPLPUSH, 这些命令都有可能造成客户端的阻塞。下面总结一下 Redis 实现阻塞和取消阻塞的过程。
阻塞过程
当一个阻塞原语的处理目标为空键时, 执行该阻塞原语的客户端就会被阻塞。有以下步骤:
1:将客户端的状态设为“正在阻塞”, 并记录阻塞这个客户端的各个键,以及阻塞的最长时限(timeout) 等数据;
2:将客户端的信息记录到 server.db[i]->blocking_keys 中(其中 i 为客户端所使用的数据库号码);
3:继续维持客户端和服务器之间的网络连接,但不再向客户端传送任何信息,造成客户端阻塞;
note: step2 中 service.db[i]->blocking_keys 是一个字典,键是那些造成客户端阻塞的键, 值是一个链表,链表里保存了所有因这个键而被阻塞的客户端,如下图所示:
阻塞的取消过程
阻塞的取消有三种方法:
【1】被动脱离:有其它客户端为造成阻塞的键推入了新元素;
【2】主动脱离:到达执行阻塞原语时设定的最大阻塞时间(timeout);
【3】强制脱离:客户端强制终止和服务端的连接,或者服务器停机;
被动脱离
阻塞因 LPUSH, RPUSH, LINSERT 等添加命令而被取消,这三个添加新元素的命令,在底层都有一个 pushGenericCommand 的函数实现(在下方源码部分增加的 TODO 标志标识关键步骤):
void lpushCommand(redisClient *c) { pushGenericCommand(c,REDIS_HEAD); } void rpushCommand(redisClient *c) { pushGenericCommand(c,REDIS_TAIL); }