golang实现redis兼容的redis服务
- 之前做的redis服务是通过tcp封装的自定义协议
原版项目地址:https://github.com/dengjiayue/my-redis.git
- 那么能不能实现一个redis兼容的redis服务,这样一般的redis包也可以调用我们写的redis服务了呢?
当然可以,需要实现redis的RESP通信协议
新版项目地址: https://github.com/dengjiayue/my-redis-v2.0-RESP-.git
实现redis兼容的redis服务思路
- 原本的数据处理模型不变,依旧使用单线程模型,map储存数据
- 实现RESP协议的支持就可以了
首先,我们需要知道redis一般收到的读写命令是什么样的去搞清楚RESP协议的原理
读
"*2\r\n$3\r\nget\r\n$4\r\nname\r\n"
写
"*3\r\n$3\r\nset\r\n$4\r\nname\r\n$8\r\nzhangsan\r\n"
RESP使用\r\n作为换行符
*2,*3表示命令的个数
一个命令包含前面一个命令数据的长度,比如$3 表示后面的数据长度为3; 然后在长度下一行才是数据;
一般第一个是方法名set,get什么的,第二个是key值,第三个是val值(如果是get就没有第三个),后面是过期时间什么的.
明白了工作原理我们就可以封装RESP协议支持了
- 根据换行符解析每一行数据
- 先解析第一行,获取整个请求的包含多少个命令
- 再解析每一个命令
- 先解析长度,再解析数据,
- 最后根据数据中的方法,key,val等消息做数据处理
- 封装返回:成功就返回"+{msg}\r\n",msg为处理结果;失败就返回"-Err {msg}\r\n",msg 为失败的信息
这样你就可以通过golang的redis包调用你的redis服务了
使用go-redis包做测试
go
import (
"context"
"fmt"
"time"
"github.com/go-redis/redis/v8"
)
// 新建连接池
func NewPool() *redis.Client {
return redis.NewClient(&redis.Options{
Addr: "localhost:8080",
PoolSize: 1,
MinIdleConns: 1,
})
}
// 写入redis
func WriteRedis(client *redis.Client) {
ctx := context.Background()
// 写入redis
rsp, err := client.Set(ctx, "name", "tom", time.Minute).Result()
if err != nil {
panic(err)
}
fmt.Println(rsp)
}
// 读取redis
func ReadRedis(client *redis.Client) {
ctx := context.Background()
// 读取redis
rsp, err := client.Get(ctx, "name").Result()
if err != nil {
panic(err)
}
fmt.Println(rsp)
}
func TestWriteRedis(t *testing.T) {
type args struct {
client *redis.Client
}
tests := []struct {
name string
args args
}{
// TODO: Add test cases.
{"test", args{NewPool()}},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
WriteRedis(tt.args.client)
})
}
}
func TestReadRedis(t *testing.T) {
type args struct {
client *redis.Client
}
tests := []struct {
name string
args args
}{
// TODO: Add test cases.
{"test", args{NewPool()}},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ReadRedis(tt.args.client)
defer tt.args.client.Close()
})
}
}
// 读写测试
func TestReadWriteRedis(t *testing.T) {
type args struct {
client *redis.Client
}
tests := []struct {
name string
args args
}{
// TODO: Add test cases.
{"test", args{NewPool()}},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
WriteRedis(tt.args.client)
ReadRedis(tt.args.client)
defer tt.args.client.Close()
})
}
}
// 读写测试
func TestReadWriteRedis(t *testing.T) {
type args struct {
client *redis.Client
}
tests := []struct {
name string
args args
}{
// TODO: Add test cases.
{"test", args{NewPool()}},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
WriteRedis(tt.args.client)
ReadRedis(tt.args.client)
defer tt.args.client.Close()
})
}
}
读写结果
=== RUN TestReadWriteRedis
=== RUN TestReadWriteRedis/test
OK
tom
--- PASS: TestReadWriteRedis/test (0.00s)
--- PASS: TestReadWriteRedis (0.00s)
PASS
ok redis_performance_test/go_redis_read_write 0.756s