0. redis通信协议
redis的客户端(redis-cli)和服务端(redis-server)的通信是建立在tcp连接之上, 两者之间数据传输的编码解码方式就是所谓的redis通信协议。所以,只要我们的redis-cli实现了这个协议的解析和编码,那么我们就可以完成所有的redis操作。
redis 协议设计的非常易读,也易于实现,关于具体的redis通信协议请参考:通信协议(protocol)。后面我们在实现这个协议的过程中也会简单重复介绍一下具体实现
1. 建立tcp连接
redis客户端和服务端的通信是建立tcp连接之上,所以第一步自然是先建立连接
package main
import (
"flag"
"log"
"net"
)
var host string
var port string
func init() {
flag.StringVar(&host, "h", "localhost", "hsot")
flag.StringVar(&port, "p", "6379", "port")
}
func main() {
flag.Parse()
tcpAddr := &net.TCPAddr{IP: net.ParseIP(host), Port: port}
conn, err := net.DialTCP("tcp", nil, tcpAddr)
if err != nil {
log.Println(err)
}
defer conn.Close()
// to be continue
}
后续我们发送和接受数据便都可以使用conn.Read()和conn.Write()来进行了
2. 发送请求
发送请求第一个第一个字节是"*",中间是包含命令本身的参数个数,后面跟着"rn" 。之后使用"$"加参数字节数量并使用"rn"结尾,然后紧跟参数内容同时也使用"rn"结尾。如执行 SET key liangwt 客户端发送的请求为"*3rn$3rnSETrn$3rnkeyrn$7rnliangwtrn"
注意:
-
命令本身也作为协议的其中一个参数来发送
rn 对应byte的十进制为 13 10
我们可以使用telnet测试下
wentao@bj:~/github.com/liangwt/redis-cli$ telnet 127.0.0.1 6379
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
*3
$3
SET
$3
key
$7
liangwt
+OK
先暂时忽略服务端的回复,通过telnet我们可以看出请求协议非常简单,所以对于请求协议的实现不做过多的介绍了,直接放代码(如下使用基于字符串拼接,只是为了更直观的演示,效率并不高,实际代码中我们使用bytes.Buffer来实现)
func MultiBulkMarshal(args ...string) string {
var s string
s = "*"
s += strconv.Itoa(len(args))
s += "rn"
// 命令所有参数
for _, v := range args {
s += "$"
s += strconv.Itoa(len(v))
s += "rn"
s += v
s += "rn"
}
return s
}
在实现了对命令和参数进行编码之后,我们便可以通过conn.Write()把数据推送到服务端
func main() {
// ....
req := MultiBulkMarshal("SET", "key", "liangwt")
_, err = conn.Write([]byte(req))
if err != nil {
log.Fatal(err)
}
// to be continue
}
3. 获取回复
我们首先实现通过tcp获取服务端返回值,就是上面提到过的conn.Read()。









