使用golang写一个redis-cli的方法示例

2020-01-28 13:38:17于海丽


func main() {
  // ....
 p := make([]byte, 1024)
 _, err = conn.Read(p)
 if err != nil {
 log.Fatal(err)
 }
 // to be continue
}

4. 解析回复

我们拿到p之后我们就可以解析返回值了,redis服务端的回复是分为几种情况的

状态回复 错误回复 整数回复 批量回复 多条批量回复

我们把前四种单独看作一组,因为他们都是单一类型的返回值

我们把最后的多条批量回复看成单独的一组,因为它是包含前面几种类型的混合类型。而且你可以发现它和我们的请求协议是一样的

也正是基于以上的考虑我们创建两个函数来分别解析单一类型和混合类型,这样在解析混合类型中的某一类型时就只需要调用单一类型解析的函数即可

在解析具体协议前我们先实现一个是读取到rn为止的函数


func ReadLine(p []byte) ([]byte, error) {
 for i := 0; i < len(p); i++ {
 if p[i] == 'r' {
  if p[i+1] != 'n' {
  return []byte{}, errors.New("format error")
  }
  return p[0:i], nil
 }
 }
 return []byte{}, errors.New("format error")
}

第一种状态回复:

状态回复是一段以 "+" 开始, "rn" 结尾的单行字符串。如 SET 命令成功的返回值:"+OKrn"

所以我们判断第一个字符是否等于 '+' 如果相等,则读取到rn


func SingleUnMarshal(p []byte) ([]byte, int, error) {
 var (
 result []byte
 err  error
 length int
 )
 switch p[0] {
 case '+':
 result, err = ReadLine(p[1:])
 length = len(result) + 3
 }

 return result, length, err
}

注:我们在返回实际回复内容的同时也返回了整个回复的长度,方便后面解析多条批量回复时定位下一次的解析位置

第二种错误回复:

错误回复的第一个字节是 "-", "rn" 结尾的单行字符串。如执行 SET key缺少参数时返回值:"-ERR wrong number of arguments for 'set' commandrn"

错误回复和状态回复非常相似,解析方式也是一样到。所以我们只需添加一个case即可


func SingleUnMarshal(p []byte) ([]byte, int, error) {
 var (
 result []byte
 err  error
 length int
 )
 switch p[0] {
 case '+', '-':
 result, err = ReadLine(p[1:])
 length = len(result) + 3
 }
 return result, length, err
}

第三种整数回复:

整数回复的第一个字节是":",中间是字符串表示的整数,"rn" 结尾的单行字符串。如执行LLEN mylist命令时返回 ":10rn"

整数回复也和上面两种是一样的,只不过返回的是字符串表示的十进制整数


func SingleUnMarshal(p []byte) ([]byte, int, error) {
 var (
 result []byte
 err  error
 length int
 )
 switch p[0] {
 case '+', '-', ':':
 result, err = ReadLine(p[1:])
 length = len(result) + 3
 }
 return result, length, err
}