This is a creation in Article, where the information may have evolved or changed.
The REDIS.V5 is a Golang-based Redis operations library that encapsulates the various operations on Redis
Source Address is
Https://github.com/go-redis/redis
The Redis client's work essentially transmits the REDIS protocol-compliant command request to Redis server based on the TCP protocol and resolves the server-side return value based on the Redis protocol
We can use the Telnet tool to simulate this process, such as a ping command that we can send requests to
$ telnet 127.0.0.1 6379Trying 127.0.0.1...Connected to 127.0.0.1.Escape character is '^]'.// 以下是发送的内容*1$4PING// 这是redis server返回内容+PONG
So to understand the Redis client, you should first familiarize yourself with the Redis protocol
The Redis protocol consists of two parts, the request protocol and the response protocol , which are very simple communication protocols, easy to program parsing and easy for human to read.
It is important to note that earlier versions of the Redis protocol are not the same as they are today, so the special reminder is that this article is based on the Redis 3.2.6 version.
Request Agreement:
* <参数数量> CR LF$ <参数 1 的字节数量> CR LF<参数 1 的数据> CR LF... $ <参数 N 的字节数量> CR LF<参数 N 的数据> CR LF
We use the initial Telnet simulation to send the ping command as an example
The number of commands for this transmission is followed by the first planetary number. 1 indicates that the request has only one parameter, the same argument is two (get key) for the get command, so it should be written as 2 for the Get parameter
followed by a pass request parameter, each parameter is represented by two lines, where the previous line $n represents the number of characters in the argument, and the next line is the string of the argument
For example, $4 indicates that the command has 4 characters, and the next line of Ping is the string representation of the command.
In the same way, the set command can be written like this
*3$3SET$3key$5value
This can be written in a byte array
"*3\r\n$3\r\nset\r\n$3key\r\n$5value\r\n"
The return value is
+OK
The description command was successfully parsed and executed
Response protocol:
Having said the request protocol, let's look at the response protocol, which is a little more complicated than the request protocol with a uniform format, because the responses to different commands are different, so let's look at each
First, the first byte of the Redis return text indicates the type of response, where the response type is as follows:
状态响应(status reply)的第一个字节是 "+"错误响应(error reply)的第一个字节是 "-"整数响应(integer reply)的第一个字节是 ":"主体响应(bulk reply)的第一个字节是 "$"批量主体响应(multi bulk reply)的第一个字节是 "*"
For example, for a ping command, if it is able to ping, it returns "+pong", which is a status response
Status response
For a status response, the general processing is the character after the client returns "+", for example, the ping command returns "PONG", and the SET command returns "OK"
Error response
The handling of the error response is similar to the status response because, in a sense, the error is a state, but a special state, so the handling of the error response is to return the character after "-"
Integer response
The integer response is to handle commands such as Incr,ttl, which return an integer directly, and the general process is to return an integer number after ":".
Principal response
The principal response is used to return a string, which is the most common form of response, such as all commands that get a string, such as a GET command, that are obtained by the principal response or by the bulk principal ring protocol.
The number after the first line of the principal response, "$", indicates the length of the returned string, and the next line returns the string literal. If the string is empty, then the first row returns "$-1"
Bulk Body Response
The bulk body response is the server-side batch return string protocol, very similar to the request protocol, the number after the first line "*" indicates how many of the strings returned this time, and then returns the string in the principal response protocol
Well, here we have a general understanding of Redis's communication protocol. Although we are in the analysis of other people write code, but the end of the paper on the light, it is known that the matter to preach, in the analysis of the source code is very useful when you personally knock some codes. So I used Golang to write a small program to simulate the Redis communication protocol, due to the relative responsibility of the response protocol, we temporarily to simulate the state response and the main response of two protocols
The Golang code is as follows:
Package Mainimport ("FMT" "OS" "Net" "StrConv") const (redisserveraddress = "127.0.0.1:6379" Redisserve Rnetwork = "tcp") type Rediserror struct {msg string}func (this *rediserror) Error () string {return this.msg}//connected to Redis Serverfunc Conn () (NET. Conn, error) {Conn, err: = Net. Dial (Redisservernetwork, redisserveraddress) if err! = Nil {fmt. Println (Err. Error ()) OS. Exit (1)} return conn, err}//converts the parameter to the REDIS request protocol func getcmd (args []string) []byte {cmdstring: = "*" + StrConv. Itoa (len (args)) + "\ r \ n" for _, V: = Range args {cmdstring + = "$" + StrConv. Itoa (Len (v)) + "\ r \ n" + V + "\ r \ n"} cmdbyte: = make ([]byte, Len (cmdstring)) copy (cmdbyte[:], cmdstring) retur n Cmdbyte}func dealreply (reply []byte) (interface{}, error) {responsetype: = reply[0] Switch Responsetype {case ' + ': return dealstatusreply (Reply) Case ' $ ': return dealbulkreply (Reply) Default:return nil, &rediserror{"Proto wrong!"} }}//Processing Status response func dealstatusreply (reply []byte) (interface{}, error) {statusbyte: = reply[1:] pos: = 0 for _, V: = Range Statusbyte {if v = = ' \ r ' {break} pos++} Status: = statusbyte[:p OS] ret Urn string (status), nil}//handles the body response func dealbulkreply (reply []byte) (interface{}, error) {statusbyte: = reply[1:]// Gets the response string length indicated in the first line of the response text pos: = 0 for _, V: = range Statusbyte {if v = = ' \ r ' {break} P os++} strlen, err: = StrConv. Atoi (String (statusbyte[:p OS])) if err! = Nil {fmt. Println (Err. Error ()) OS. Exit (1)} if strlen = =-1 {return ' nil ', nil} nextlinepost: = 1 for _, V: = range Statusbyte {if V = = ' \ n ' {break} nextlinepost++} Result: = String (statusbyte[nextlinepost:nextlinepost+s Trlen]) return result, Nil}func main () {args: = os. Args[1:] If len (args) = = 0 {fmt. Println ("Usage:go RUn proto.go + redis command\nfor Example:\ngo run proto.go PING ") os. Exit (0)} conn, _: = conn () cmd: = Getcmd (args) conn. Write (cmd) BUF: = Make ([]byte, 1024x768) n, _: = conn. Read (BUF) Res, _: = Dealreply (Buf[:n]) fmt. Println ("Redis's return result is", RES)}
To run the code:
// 测试PING命令$go run proto.go PINGredis的返回结果是 PONG// 测试SET命令$go run proto.go SET key valueredis的返回结果是 OK// 测试GET命令(GET一个存在的键)$go run proto.go GET key redis的返回结果是 value// 测试GET命令(GET一个不存在的键)$go run proto.go GET not_exist_key redis的返回结果是 nil
All ok!
PS: This test code is very sloppy, many exceptions are not considered, mainly to test the understanding of Redis
Article reference
Http://doc.redisfans.com/topic/protocol.html