Go to learn about Redis communication protocol

  golang, php, redis

Go to learn about Redis communication protocol

Original address:Go to learn about Redis communication protocol

Go, PHP, Java … all have so many packages to support your use of Redis, have you ever thought about that?

With the server and the client, how do they communicate and what communication protocol do they interact with each other?

Introduction

Based on our purpose, this article mainly explains and practices the communication protocol of Redis.

Redis’ client and server exchange data through TCP connection, and the server’s default port number is 6379

Commands or data sent by clients and servers will always be based on\r\n(CRLF) End (This is a Convention)

Protocol

In Redis, it is divided intoRequestAndReply, and the request protocol is divided into new and old versions. The new unified request protocol was introduced in Redis version 1.2 and eventually became the standard communication method for Redis server in Redis version 2.0

This article is based on the new version of the protocol to realize the function, and it is not recommended to use the old version (1.2 is quite old). The following are various examples of the new agreement:

Request protocol

1. Format Example

*<参数数量> CR LF
$<参数 1 的字节数量> CR LF
<参数 1 的数据> CR LF
...
$<参数 N 的字节数量> CR LF
<参数 N 的数据> CR LF

Under this protocol, all parameters sent to the Redis server are binary safe.

2. Printing Examples

*3
$3
SET
$5
mykey
$7
myvalue

3. Actual Agreement Value

"*3\r\n$3\r\nSET\r\n$5\r\nmykey\r\n$7\r\nmyvalue\r\n"

This is the request protocol specification of Redis. The client logic is written according to Example 1, and the final message is Example 3. I believe you already have a general concept. Redis’ protocol is very concise and easy to understand. This is also one of the reasons why it is easy to use. Can you think about the benefits of this definition of protocol?

Reply

Redis will return a variety of different types of responses depending on the protocol you request (and the results of the commands you execute). In this reply “protocol”, you can determine what type of reply is by checking the first byte, as follows:

  • The first byte of the status reply is “+”
  • The first byte of the error reply is “”
  • The first byte of integer reply is “:”
  • The first byte of a bulk reply is “$”
  • The first byte of multi bulk reply is “*”

With the head mark of the reply and the CRLF at the end, you can roughly guess what the reply “agreement” is like, but the truth can only be obtained through practice, knowing that even you will forget it soon.

Practice

Interact with Redis server

package main

import (
    "log"
    "net"
    "os"

    "github.com/EDDYCJY/redis-protocol-example/protocol"
)

const (
    Address = "127.0.0.1:6379"
    Network = "tcp"
)

func Conn(network, address string) (net.Conn, error) {
    conn, err := net.Dial(network, address)
    if err != nil {
        return nil, err
    }

    return conn, nil
}

func main() {
        // 读取入参
    args := os.Args[1:]
    if len(args) <= 0 {
        log.Fatalf("Os.Args <= 0")
    }
    
        // 获取请求协议
    reqCommand := protocol.GetRequest(args)
    
    // 连接 Redis 服务器
    redisConn, err := Conn(Network, Address)
    if err != nil {
        log.Fatalf("Conn err: %v", err)
    }
    defer redisConn.Close()
    
        // 写入请求内容
    _, err = redisConn.Write(reqCommand)
    if err != nil {
        log.Fatalf("Conn Write err: %v", err)
    }
    
        // 读取回复
    command := make([]byte, 1024)
    n, err := redisConn.Read(command)
    if err != nil {
        log.Fatalf("Conn Read err: %v", err)
    }
    
        // 处理回复
    reply, err := protocol.GetReply(command[:n])
    if err != nil {
        log.Fatalf("protocol.GetReply err: %v", err)
    }
    
        // 处理后的回复内容
    log.Printf("Reply: %v", reply)
    // 原始的回复内容
    log.Printf("Command: %v", string(command[:n]))
}

Here, we have completed the entire process of interaction between Redis client and server, as follows:

1. Read command line parameters: obtain the executed Redis command

2, acquiring request protocol parameters

3. Connect the Redis server and obtain the connection handle

4. Write the request protocol parameters into the connection: send the command line parameters of the request

5. read the returned data from the connection: read the previously requested reply data

6, according to the reply “agreement” content, processing the reply data set

7. Output the processed reply content and the original reply content

Request

func GetRequest(args []string) []byte {
    req := []string{
        "*" + strconv.Itoa(len(args)),
    }

    for _, arg := range args {
        req = append(req, "$"+strconv.Itoa(len(arg)))
        req = append(req, arg)
    }

    str := strings.Join(req, "\r\n")
    return []byte(str + "\r\n")
}

Through the analysis of Redis’ request protocol, its rule can be obtained. First add the flag bit, calculate the total number of parameters, recycle and merge the byte number and value of each parameter.

Reply

func GetReply(reply []byte) (interface{}, error) {
    replyType := reply[0]
    switch replyType {
    case StatusReply:
        return doStatusReply(reply[1:])
    case ErrorReply:
        return doErrorReply(reply[1:])
    case IntegerReply:
        return doIntegerReply(reply[1:])
    case BulkReply:
        return doBulkReply(reply[1:])
    case MultiBulkReply:
        return doMultiBulkReply(reply[1:])
    default:
        return nil, nil
    }
}

func doStatusReply(reply []byte) (string, error) {
    if len(reply) == 3 && reply[1] == 'O' && reply[2] == 'K' {
        return OkReply, nil
    }

    if len(reply) == 5 && reply[1] == 'P' && reply[2] == 'O' && reply[3] == 'N' && reply[4] == 'G' {
        return PongReply, nil
    }

    return string(reply), nil
}

func doErrorReply(reply []byte) (string, error) {
    return string(reply), nil
}

func doIntegerReply(reply []byte) (int, error) {
    pos := getFlagPos('\r', reply)
    result, err := strconv.Atoi(string(reply[:pos]))
    if err != nil {
        return 0, err
    }

    return result, nil
}

...

Here, we have distributed all reply types, and different reply flag bits correspond to different processing methods. Here, we need to pay attention to several issues, as follows:

1. When the requested value does not exist, the special value -1 will be used as a reply.

2. All strings sent by the server end with CRLF

3. Multiple batch responses can be based on batch responses. Attention should be paid to understanding

4. Multiple batch replies without content exist

Most importantly, controlling the rules of different replies can help you better understand the interactive process of Redis’ request and reply.

Summary

The reason for writing this article is that often when using Redis, you just use it. You don’t know what kind of communication protocol it is based on. This feeling is very uncomfortable.

Through the explanation of this article, I believe that you have roughly understood how Redis client interacts with the server and the communication principle it uses. I hope it can be helpful to you!

Finally, if you want to check the code in detail, turn right project address:https://github.com/EDDYCJY/re …

If it is helpful to you, welcome to Star.

References