Redis的resp協議

resp協議

redis客戶端和服務端交互使用的是redis做者制定的一個協議,叫resp(REdis Serialization Protocol)。git

具體分以下幾個層次github

  • 基於tcp
  • 請求響應模式,但在兩種狀況下再也不是簡單的請求和響應模式(下文介紹)
  • 支持五種類型的數據,分別是簡單字符串,錯誤,整型,bulk strings ,數組

客戶端發給服務端的命令都會序列化爲array,而服務端返回給客戶端的能夠爲如上任意一種類型,各簡單舉例以下redis

  • 簡單字符串,第一個byte爲 '+',例如 "+OKrn"
  • 錯誤,第一個byte爲'-',例如 "-Error messagern"
  • 整型, 第一個byte爲':",例如 ":10rn"
  • bulk strings,第一個byte爲"$",例如 "$6rnfoobarrn"(此處爲美圓符號,沒有前邊的反斜槓)
  • 數組,第一個byte爲'',例如"2rn$3\r\nfoo\r\n$3rnbarrn"

具體介紹參考:https://redis.io/topics/protocol數組

sub pattern

請求響應模式有兩種特殊狀況tcp

  • pipeline模式,客戶端同時發送多條命令到服務端
  • sub pattern:當客戶端執行subscribe命令後,再也不要求客戶端發送命令,當有其餘客戶端在訂閱渠道上publish消息後,服務端會主動push信息到客戶端

咱們拿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...

相關文章
相關標籤/搜索