我试用了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