go redis 协议篇

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\n
key\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\n
key\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,
    }
}

你可能感兴趣的:(go redis 协议篇)