func MultiUnMarsh(p []byte) ([][]byte, error) {
if p[0] != '*' {
return [][]byte{}, errors.New("format error")
}
n, err := ReadLine(p[1:])
if err != nil {
return [][]byte{}, err
}
l, err := strconv.Atoi(string(n))
if err != nil {
return [][]byte{}, err
}
// 多条批量回复也可以是空白的(empty)
if l == 0 {
return [][]byte{}, nil
}
// 无内容的多条批量回复(null multi bulk reply)也是存在的,
// 客户端库应该返回一个 null 对象, 而不是一个空数组。
if l == -1 {
return nil, nil
}
result := make([][]byte, l)
t := len(n) + 3
for i := 0; i < l; i++ {
ret, length, err := SingleUnMarshal(p[t:])
if err != nil {
return [][]byte{}, errors.New("format error")
}
result[i] = ret
t += length
}
return result, nil
}
5. 命令行模式
一个可用的redis-cli自然是一个交互式的,用户输入指令然后输出返回值。在go中我们可以使用以下代码即可获得一个类似的交互式命令行
func main() {
// ....
for {
fmt.Printf("%s:%d>", host, port)
bio := bufio.NewReader(os.Stdin)
input, _, err := bio.ReadLine()
if err != nil {
log.Fatal(err)
}
fmt.Printf("%sn", input)
}
}
我们运行以上代码就可以实现
localhost:6379>set key liang
set key liang
localhost:6379>get key
get key
localhost:6379>
结合上我们的redis发送请求和解析请求即可完成整个redis-cli
func main() {
// ....
for {
fmt.Printf("%s:%d>", host, port)
// 获取输入命令和参数
bio := bufio.NewReader(os.Stdin)
input, err := bio.ReadString('n')
if err != nil {
log.Fatal(err)
}
fields := strings.Fields(input)
// 编码发送请求
req := MultiBulkMarshal(fields...)
// 发送请求
_, err = conn.Write([]byte(req))
if err != nil {
log.Fatal(err)
}
// 读取返回内容
p := make([]byte, 1024)
_, err = conn.Read(p)
if err != nil {
log.Fatal(err)
}
// 解析返回内容
if p[0] == '*' {
result, err := MultiUnMarsh(p)
} else {
result, _, err := SingleUnMarshal(p)
}
}
// ....
}
6. 总结
到目前为止我们的cli程序已经全部完成,但其实还有很多不完美地方。但核心的redis协议解析已经完成,使用这个解析我们能完成任何的cli与服务器之间的交互
更详细的redis-cli实现可以参考我的github:A Simaple redis cli - Rclient
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持易采站长站。









