做者:张仕华git
redis客户端和服务端交互使用的是redis做者制定的一个协议,叫resp(REdis Serialization Protocol)。github
具体分以下几个层次redis
客户端发给服务端的命令都会序列化为array,而服务端返回给客户端的能够为如上任意一种类型,各简单举例以下shell
具体介绍参考:https://redis.io/topics/protocol数组
请求响应模式有两种特殊状况tcp
咱们拿redis-cli客户端试一下执行subscribe测试
127.0.0.1:6379> subscribe foo Reading messages... (press Ctrl-C to quit) 1) "subscribe" 2) "foo" 3) (integer) 1
能够看到,redis-cli会阻塞,当另起一个客户端,publish消息后,会收到该消息并打印ui
~$redis-cli 127.0.0.1:6379> publish foo bar (integer) 1
~$redis-cli 127.0.0.1:6379> subscribe foo Reading messages... (press Ctrl-C to quit) 1) "subscribe" 2) "foo" 3) (integer) 1 1) "message" 2) "foo" 3) "bar"
直观感受是服务端阻塞了,没有返回数据给客户端。但看redis源码,实际服务端并无阻塞,而且能够在链接上继续接收并处理命令this
void subscribeCommand(client *c) { int j; //将订阅的渠道加入相应结构体并直接返回 for (j = 1; j < c->argc; j++) pubsubSubscribeChannel(c,c->argv[j]); //将客户端置CLINET_PUBSUB标记 c->flags |= CLIENT_PUBSUB; }
当客户端置了CLINET_PUBSUB标记后,命令处理会作以下特殊逻辑code
int processCommand(client *c) { ... //当置CLIENT_PUBSUB标记后,只有ping/subscribe/unsubscribe/psubscribe/punsubscribe命令可以执行 if (c->flags & CLIENT_PUBSUB && c->cmd->proc != pingCommand && c->cmd->proc != subscribeCommand && c->cmd->proc != unsubscribeCommand && c->cmd->proc != psubscribeCommand && c->cmd->proc != punsubscribeCommand) { addReplyError(c,"only (P)SUBSCRIBE / (P)UNSUBSCRIBE / PING / QUIT allowed in this context"); return C_OK; } ... }
如上文所示,当客户端执行subscribe命令后,其实是能够继续订阅或者取消订阅渠道,而且能够执行ping命令。redis-cli客户端其实是本身阻塞了,以下代码:
if (config.pubsub_mode) { if (config.output != OUTPUT_RAW) printf("Reading messages... (press Ctrl-C to quit)\n"); //进入死循环,一直等待服务端发送消息 while (1) { if (cliReadReply(output_raw) != REDIS_OK) exit(1); } }
那么,咱们能够拿go写一个不阻塞的版本,而且能够测试redis的subscribe 模式。效果以下
>bogon:godis-cli $ go run godis-cli.go > set k1 v1 OK > get k1 v1 > subscribe foo subscribe foo 1 [sub]>subscribe foo1//sub模式下能够继续订阅其余渠道 subscribe foo1 2 [sub]> unsubscribe foo1//取消订阅 unsubscribe foo1 1 [sub]> ping//sub模式也能够执行ping pong [sub]> get k1 //sub模式下不能执行get命令 Redis Error: only (P)SUBSCRIBE / (P)UNSUBSCRIBE / PING / QUIT allowed in this context [sub]> exit exit sub pattern.... >get k1//退出sub模式后能够继续正常执行get命令 v1 > exit
因为godis-cli直接实现了RESP协议,虽然只是为了观察subscribe pattern的效果,但实际上能够支持全部redis命令的执行
具体代码地址见:https://github.com/erpeng/god...