1 Redis网络协议详解
redis的网路协议全名是Redis Serialization Protocol (RESP), 它设计五项,如下所示:
- 正常回复
- 错误回复
- 整数
- 多行字符串
- 数组
(1)正常回复
正常回复以"+"开头,以"\r\n"结尾的字符串形式
(2) 错误回复
错误回复以"-" 开头,以"\r\n" 结尾的字符串形式
(3) 整数
整数以":"开头,以"\r\n"结尾的字符串形式
(4) 多行字符串
多行字符串以"11\r\nhello.world\r\n, 首先11代表hello.world的长度11个字节,后面紧跟\r\n, 再然后就是数据内容,最后再紧跟\r\n
为什么要在字符串长度后面跟一个\r\n ?
假设你需要处理的是空字符串 "" , 那么就是14\r\nhello\r\nworld\r\n
(5)数组 多个多行字符串
以""开头,后跟成员个数
举例: SET key value
3\r\n3\r\nkey\r\n3\r\nSET\r\n, 在接着是第2个key: 5\r\nvalue**\r\n
2 实现常见的几种正常Reply函数
首先上面分析了redis的协议,也了解了一些数据的回复,这里实现一些常见的一些Reply。
接口定义Reply:
package resp
// Reply 代表服务端对客户端的回复
type Reply interface {
// ToBytes 因为tcp协议里面的读写都是针对字节的,所以这里都要转为字节
ToBytes() []byte
}
实现PongReply:
package reply
// PongReply redis客户端输入一个PING Server端返回一个PONG
type PongReply struct {
}
var pongBytes = []byte("+PONG\r\n")
func (p *PongReply) ToBytes() []byte {
return pongBytes
}
func MakePongReply() *PongReply {
return &PongReply{}
}
实现OkReply:
type OkReply struct {
}
var okBytes = []byte("+OK\r\n")
func (o *OkReply) ToBytes() []byte {
return okBytes
}
// 这样无需每次make的时候都去创建OkReply对象
var okReply = &OkReply{}
func MakeOkReply() *OkReply {
return okReply
}
实现空字符串Reply:是NULL的空,不是""的空
// NullBulkReply 服务端的空回复
type NullBulkReply struct {
}
var nullBulkReply = []byte("$-1\r\n")
func (n *NullBulkReply) ToBytes() []byte {
return nullBulkReply
}
func MakeNullBulkReply() *NullBulkReply {
return &NullBulkReply{}
}
实现空数组的Reply:
// EmptyMultiBulkReply 服务端空数组的回复
type EmptyMultiBulkReply struct {
}
var emptyMultiBulkReplyBytes = []byte("*0\r\n")
func (e *EmptyMultiBulkReply) ToBytes() []byte {
return emptyMultiBulkReplyBytes
}
func MakeEmptyMultiBulkReply() *EmptyMultiBulkReply {
return &EmptyMultiBulkReply{}
}
实现NoReply:
type NoReply struct {
}
var noReplyBytes = []byte("")
func (n *NoReply) ToBytes() []byte {
return noReplyBytes
}
func MakeNoReply() *NoReply {
return &NoReply{}
}
3 实现常见的几种错误Reply函数
- UnknownErrReply 未知错误
// UnknownErrReply 未知错误
type UnknownErrReply struct {
}
var unKnownErrBytes = []byte("-Err unknown\r\n")
func (u *UnknownErrReply) Error() string {
return "Err unknown"
}
func (u *UnknownErrReply) ToBytes() []byte {
return unKnownErrBytes
}
func MakeUnkownErrReply() *UnknownErrReply {
return &UnknownErrReply{}
}
- 参数个数异常,比如SET KEY VALUE 需要三个参数,但是只传2个,则参数个数有问题
// ArgNumErrReply 参数个数异常,比如SET KEY VALUE 需要三个参数,但是只传2个,则参数个数有问题
type ArgNumErrReply struct {
Cmd string // 记录那个客户端的指令
}
func (a *ArgNumErrReply) Error() string {
return "Err wrong number of arguments for '" + a.Cmd + "' command\r\n"
}
func (a *ArgNumErrReply) ToBytes() []byte {
return []byte("-Err wrong number of arguments for '" + a.Cmd + "' command\r\n")
}
func MakeArgNumErrReply(cmd string) *ArgNumErrReply {
return &ArgNumErrReply{
Cmd: cmd,
}
}
- 语法错误
// SyntaxErrReply 语法错误
type SyntaxErrReply struct {
}
var syntaxErrBytes = []byte("-Err syntax error\r\n")
func (s *SyntaxErrReply) Error() string {
return "Err syntax error"
}
func (s *SyntaxErrReply) ToBytes() []byte {
return syntaxErrBytes
}
var syntaxErrReply = &SyntaxErrReply{}
func MakeSyntaxErrReply() *SyntaxErrReply {
return syntaxErrReply
}
- 数据类型错误
// WrongTypeErrReply 数据类型错误
type WrongTypeErrReply struct {
}
var wrongTypeErrBytes = []byte("-Err wrong type operation against a key holding the wrong kind of value\r\n")
func (w *WrongTypeErrReply) Error() string {
return "wrong type operation against a key holding the wrong kind of value"
}
func (w *WrongTypeErrReply) ToBytes() []byte {
return wrongTypeErrBytes
}
var wrongTypeErrRepley = new(WrongTypeErrReply)
func MakeWrongTypeErrorReply() *WrongTypeErrReply {
return wrongTypeErrRepley
}
- 协议错误
// ProtocolErrReply 协议错误
type ProtocolErrReply struct {
Msg string
}
func (p *ProtocolErrReply) Error() string {
return "Err protocol error: " + p.Msg
}
func (p *ProtocolErrReply) ToBytes() []byte {
return []byte("-Err protocol error: '" + p.Msg + "'\r\n")
}
func MakeProtocolErrReply(msg string) *ProtocolErrReply {
return &ProtocolErrReply{
Msg: msg,
}
}
4 自定义Reply函数
首先第一节讲了5中协议的使用情况,这里针对这5中情况进行自定义Reply实现:
redis的网路协议全名是Redis Serialization Protocol (RESP), 它设计五项,如下所示:
- 正常回复
- 错误回复
- 整数
- 多行字符串
- 数组
(1)正常回复
正常回复以"+"开头,以"\r\n"结尾的字符串形式
// StatusReply 正常答复
type StatusReply struct {
Status string
}
func (s *StatusReply) ToBytes() []byte {
return []byte("+" + s.Status + CRLF)
}
func MakeStatusReply(status string) *StatusReply {
return &StatusReply{
Status: status,
}
}
(2) 错误回复
错误回复以"-" 开头,以"\r\n" 结尾的字符串形式
// ErrorReply 定义redis错误的回复接口
type ErrorReply interface {
Error() string
ToBytes() []byte
}
// StandardErrReply 标准错误答复
type StandardErrReply struct {
Status string
}
func (s *StandardErrReply) Error() string {
return s.Status
}
func (s *StandardErrReply) ToBytes() []byte {
return []byte("-" + s.Status + CRLF)
}
func MakeStandardErrReply(status string) *StandardErrReply {
return &StandardErrReply{
Status: status,
}
}
(3) 整数
整数以":"开头,以"\r\n"结尾的字符串形式
// IntReply 整数答复
type IntReply struct {
Code int64
}
func (i *IntReply) ToBytes() []byte {
return []byte(":" + strconv.FormatInt(i.Code, 10) + CRLF)
}
func MakeIntReply(code int64) *IntReply {
return &IntReply{
Code: code,
}
}
(4) 多行字符串
多行字符串以"11\r\nhello.world\r\n, 首先11代表hello.world的长度11个字节,后面紧跟\r\n, 再然后就是数据内容,最后再紧跟\r\n
为什么要在字符串长度后面跟一个\r\n ?
假设你需要处理的是空字符串 "" , 那么就是14\r\nhello\r\nworld\r\n
var (
nullBulkReply = "$-1"
CRLF = "\r\n"
)
// BulkReply 自定义字符串的Reply
type BulkReply struct {
// 字符串的值,比如hello, 那么reply 就是$5hello\r\n,相当于自动转换这个过程
Arg []byte
}
func (b *BulkReply) ToBytes() []byte {
if len(b.Arg) == 0 {
return []byte(nullBulkReply + CRLF)
}
return []byte("$" + strconv.Itoa(len(b.Arg)) + CRLF + string(b.Arg) + CRLF)
}
// MakeBulkReply 外面传入一个字符串,自动拼装返回值
func MakeBulkReply(arg string) *BulkReply {
return &BulkReply{
Arg: []byte(arg),
}
}
(5)数组 多个多行字符串
以""开头,后跟成员个数
举例: SET key value
3\r\n3\r\nkey\r\n3\r\nSET\r\n, 在接着是第2个key: 5\r\nvalue**\r\n
// MultiBulkReply 多字符串的自定义封装
type MultiBulkReply struct {
Args [][]byte
}
func (m *MultiBulkReply) ToBytes() []byte {
argNum := len(m.Args)
var buf bytes.Buffer
buf.WriteString("*" + strconv.Itoa(argNum) + CRLF)
for _, arg := range m.Args {
if arg == nil {
buf.WriteString(nullBulkReply + CRLF)
} else {
buf.WriteString("$" + strconv.Itoa(len(arg)) + CRLF + string(arg) + CRLF)
}
}
return buf.Bytes()
}
func MakeMultiBulkReply(args [][]byte) *MultiBulkReply {
return &MultiBulkReply{
Args: args,
}
}