redis客戶端和服務端交互使用的是redis做者制定的一個協議,叫resp(REdis Serialization Protocol)。git
具體分以下幾個層次github
客戶端發給服務端的命令都會序列化爲array,而服務端返回給客戶端的能夠爲如上任意一種類型,各簡單舉例以下redis
具體介紹參考: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
xiaoju@zsh_test ~$redis-cli 127.0.0.1:6379> publish foo bar (integer) 1 127.0.0.1:6379> xiaoju@zsh_test ~$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; } ... }
godis-cli
如上文所示,當客戶端執行subscribe命令後,其實是能夠繼續訂閱或者取消訂閱渠道,而且能夠執行ping命令。redis-cli客戶端其實是本身阻塞了,以下代碼:ip
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 didi$ 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...