我试用了Redis公式的Go客户端库rueidis
這篇文章是什麼?
我在Twitter上随便看的时候发现Redis官方发布了适用于Go语言的客户端库,于是我想知道它有多容易地集成到我的项目中,于是就试了试。
根据公式所说,它比当前使用的go-redis更快!✨
我只是做了一些基本简单的事情,但在移植时可能需要的HSet/HGetAll也尝试写了一下。
公式链接可以在GitHub找到:https://github.com/redis/rueidis。
构造
为了更容易地将其应用于自己的项目,Redis操作以repository的形式进行了实现。
Redis连接和客户端生成在infra目录中的redis.go文件中。
此外,为了启动Redis,还编写了docker-compose.yaml文件。
.
├─ main.go
├─ infra
│ ├─ redis.go
├─ repository
│ ├─ cache_repository.go
├─ docker-compose.yaml
├─ .env
代码
大约经过3个小时的时间,我同时比较了官方和自己的项目,并且由于对Redis不太熟悉,所以在查看Redis命令的同时进行了奋斗,最终的结果如下所示。
主要功能.go
我想使用godotenv来读取.env文件,这是为了将Redis的主机和端口根据环境进行管理的代码。我通过infra.Redis()来生成客户端并创建Repository。
然后,只是通过Repository的SetEx、Get、HSet和HGetAll来读取结果,并用fmt.Println(f)打印出来。
package main
import (
"context"
"fmt"
"handson-ruieidis/infra"
"handson-ruieidis/repository"
"log"
"github.com/joho/godotenv"
)
type account struct {
Name string
Age string
}
func main() {
err := godotenv.Load(".env")
if err != nil {
panic("Error loading .env file")
}
repo := repository.NewCacheRepository(infra.Redis())
defer infra.Redis().Close()
ctx := context.Background()
// SET key1 val1
if err := repo.SetEx(ctx, "key1", "val1", 65535); err != nil {
log.Fatal(err)
}
// GET key1
val, err := repo.Get(ctx, "key1")
if err != nil {
log.Fatal(err)
}
fmt.Println("Get key1:", val) // "val1"
hSetKey := "account_1"
sampleAccount := &account{
Name: "kojikoji",
Age: "41",
}
if err := repo.HSet(ctx, hSetKey, sampleAccount); err != nil {
log.Fatal(err)
}
hGetAll, err := repo.HGetAll(ctx, hSetKey)
if err != nil {
log.Fatal(err)
}
for key, value := range hGetAll {
fmt.Printf("key:%s\nvalue:%s\n", key, value)
}
}
存储库/缓存存储库.go
在使用go-redis时,命令的构建方式略有不同,采用了commandBuild形式。刚开始会感到不习惯,但最后已经能够流畅地书写了。出乎意料。
然而,在使用HSet传递结构体的用例中,当我尝试使用循环一次性将所有字段和值设置到FieldValue中并进行client.Do操作时,出现了错误,因此我现在将其改为逐个字段进行HSet的方式。
在官方文档中,可能有人创建了类似Go-redis的API适配器,我也参考了那个适配器,但因为执行时间不足而无法成功,所以我想再试一次。
package repository
import (
"context"
"reflect"
"time"
"github.com/redis/rueidis"
)
type CasheRepository interface {
SetEx(ctx context.Context, key string, value string, ttl int64) error
Get(ctx context.Context, key string) (string, error)
HSet(ctx context.Context, key string, target interface{}) error
HGetAll(ctx context.Context, key string) (map[string]string, error)
}
type casheRepository struct {
client rueidis.Client
}
func NewCacheRepository(client rueidis.Client) CasheRepository {
repo := &casheRepository{
client: client,
}
return repo
}
func (r *casheRepository) SetEx(ctx context.Context, key string, value string, ttl int64) error {
return r.client.Do(ctx, r.client.B().Setex().Key(key).Seconds(ttl).Value(value).Build()).Error()
}
func (r *casheRepository) Get(ctx context.Context, key string) (string, error) {
result := r.client.Do(ctx, r.client.B().Get().Key(key).Build())
return result.ToString()
}
func (r *casheRepository) HSet(ctx context.Context, key string, target interface{}) error {
expire := 86400
val := reflect.ValueOf(target).Elem()
for i := 0; i < val.NumField(); i++ {
hSetFieldValue := r.client.B().Hset().Key(key).FieldValue()
typeField := val.Type().Field(i)
fieldName := typeField.Name
fieldValue := val.FieldByName(typeField.Name).Interface()
hSetFieldValue = hSetFieldValue.FieldValue(fieldName, fieldValue.(string))
if err := r.client.Do(ctx, hSetFieldValue.Build()).Error(); err != nil {
return err
}
}
ttl := int64(expire * int(time.Second))
if err := r.client.Do(ctx, r.client.B().Expire().Key(key).Seconds(ttl).Build()).Error(); err != nil {
return err
}
return nil
}
func (r *casheRepository) HGetAll(ctx context.Context, key string) (map[string]string, error) {
result := r.client.Do(ctx, r.client.B().Hgetall().Key(key).Build())
return result.AsStrMap()
}
施加力量
结果以下列方式输出。
由于只进行了简单的操作,速度等并不需要进行比较,当然会在瞬间显示出来。??
$ go run main.go
Get key1: val1
key:field
value:value
key:Name
value:kojikoji
key:Age
value:41
印象
平时我并不怎么在业务之外的领域写代码,但是在考虑如何将其应用于自己的项目和工作中时,写代码确实是很有趣的。虽然我基本上只是浏览 Twitter,但是如果有一些看起来可以应用到自己的项目中的有趣东西,我觉得试一试也不错。
成品
既然如此,我在GitHub上创建了一个仓库。
https://github.com/bayobayo0324/handson-ruieidis/tree/main