通过实现一个简单的K–V存储数据库,学习Go语言(四)
为了能够和网络中的 key-value 存储交互,我们创建自定义的 TCP 协议。
您将需要为 key-value 存储的每一个函数定义关键字。
为简单起见,每个关键字都跟着相关数据。大多数命令的结果将是成功或失败消息
通过TCP连接,实现myRedis
客户端与服务端
- 服务端
目前存在小BUG,待优化——如客户端断开重连问题等
package main
import (
"bufio"
"encoding/gob"
"fmt"
"net"
"os"
"strings"
)
var DATA = map[string]string{}
var DATAFILE = "dataFile.gob"
//myRedis基础功能
//启动应用时,加载数据
func init() {
err := load()
if err != nil {
fmt.Println(err)
}
}
func load() error {
loadFile, err := os.Open(DATAFILE)
defer loadFile.Close()
if err != nil {
fmt.Println("Empty key/value store!")
return err
}
//反序列化读取文件
decoder := gob.NewDecoder(loadFile)
//解码数据
decoder.Decode(&DATA)
fmt.Println("初始化:", DATA)
return nil
}
func SET(key string, value string) string {
if _, ok := DATA[key]; !ok {
DATA[key] = value
return ""
}
return "error: existence of this key!"
}
func GET(key string) string {
if _, ok := DATA[key]; !ok {
return "error: not found this key!"
}
return DATA[key]
}
func DELETE(key string) string {
if _, ok := DATA[key]; !ok {
return "error: not found this key!"
}
delete(DATA, key)
return ""
}
func UPDATE(key string, value string) string {
if _, ok := DATA[key]; !ok {
return "error: not found this key!"
}
DATA[key] = value
return ""
}
func SHOW() string {
str := ""
for k, v := range DATA {
str += fmt.Sprintf("%s:%s ", k, v)
}
return str
}
//数据持久化
//保存数据在磁盘上
func save() error {
//去除原数据文件,保存为新数据文件
err := os.Remove(DATAFILE)
if err != nil {
fmt.Println(err)
}
saveFile, err := os.Create(DATAFILE)
if err != nil {
fmt.Println("Cannot create", DATAFILE)
return err
}
defer saveFile.Close()
//序列化存储数据到文件
encoder := gob.NewEncoder(saveFile)
//转码数据
err = encoder.Encode(DATA)
fmt.Println(DATA, saveFile, DATAFILE)
if err != nil {
fmt.Println("Cannot save to", DATAFILE)
return err
}
return nil
}
func myRedis(text string) string {
cmd := []string{}
cmd = strings.Split(text, " ")
switch cmd[0] {
case "SET":
s := SET(cmd[1], cmd[2])
if s == "" {
break
}
return s
case "GET":
s := GET(cmd[1])
if s == "" {
break
}
return s
case "SHOW":
s := SHOW()
if s == "" {
break
}
return s
case "DELETE":
s := DELETE(cmd[1])
if s == "" {
break
}
return s
case "UPDATE":
s := UPDATE(cmd[1], cmd[2])
if s == "" {
break
}
return s
case "STOP":
err := save()
if err != nil {
fmt.Println(err)
}
default:
return "Unknown command - please try again!"
}
return "sucess"
}
func main() {
//net.Listen() 函数用于监听连接,如果第二个参数不包含 IP 地址,只有端口号的话,net.Listen()
//将监听本地系统上所有可用的 IP 地址。
listen, err := net.Listen("tcp", "localhost:5000")
fmt.Println("myRedis localhost:5000 启动...")
if err != nil {
fmt.Println(err)
return
}
defer listen.Close()
conn, err := listen.Accept()
if err != nil {
fmt.Println(err)
return
}
fmt.Println("=客户端连接成功啦!")
for {
//获取客户端发来的消息
data, err := bufio.NewReader(conn).ReadString('\n')
if err != nil {
fmt.Println(err)
return
}
sendMsg := myRedis(strings.TrimSpace(data))
fmt.Println("接受客户端消息为:", strings.TrimSpace(data))
//发送给客户端
if sendMsg != "" {
conn.Write([]byte(sendMsg + "\n"))
}
}
}
- 客户端
package main
import (
"bufio"
"fmt"
"net"
"os"
)
func main() {
//连接服务端
conn, err := net.Dial("tcp", "localhost:5000")
if err != nil {
fmt.Println(err)
return
}
for {
//通过io输入消息给服务端
reader := bufio.NewReader(os.Stdin)
fmt.Print(">> ")
//获取每一行输入
text, _ := reader.ReadString('\n')
//标准格式,通过conn数据发送
//fmt.Fprintf(conn, text)
conn.Write([]byte(text))
//读取接受消息
acceptMsg, _ := bufio.NewReader(conn).ReadString('\n')
fmt.Println(acceptMsg)
}
}
测试结果
- 客户端输入
>> SHOW
nil
>> SET test 1
sucess
>> SET test1 2
sucess
>> SET test2 3
sucess
>> SHOW
test1:2 test2:3 test:1
//保存
>> STOP
sucess
//手动断开连接,重启
>> SHOW
test:1 test1:2 test2:3
>>
- 服务端输出
myRedis localhost:5000 启动...
客户端连接成功啦!
接受客户端消息为: SHOW
接受客户端消息为: SET test 1
接受客户端消息为: SET test1 2
接受客户端消息为: SET test2 3
接受客户端消息为: SHOW
//保存
map[test:1 test1:2 test2:3] &{0xc00010f180} dataFile.gob
接受客户端消息为: STOP
//手动断开连接,重启
myRedis localhost:5000 启动...
客户端连接成功啦!
接受客户端消息为: SHOW