1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Redis公式のGoクライアントライブラリrueidisを試してみた

Posted at

This 記事 is 何?

Twitterぼんやり見てたらRedis公式のGo用クライアントライブラリが出てたとかで、自身のプロジェクトにどの程度簡単に入れられるのかなーと思い試してみました。

公式によると今使っているgo-redisよりも速い!とのことだったので✨
基本かんたんなことしかやってないですが、移植の際に必要になりそうなHSet/HGetAllも書いてみています。

公式:https://github.com/redis/rueidis

構造

自身のプロジェクトに適用するイメージが湧きやすいよう、Redis操作はrepositoryの形で実装しています。
Redis接続とClient生成はinfraディレクトリに格納したredis.goですね。
あと、Redisを立ち上げるためにdocker-compose.yamlも書いています。

.
├─ main.go
├─ infra
│   ├─ redis.go
├─ repository
│   ├─ cache_repository.go
├─ docker-compose.yaml
├─ .env

コード

3時間ほど、公式と自身のプロジェクトを見比べながら、あとRedisにも精通してなかったのでRedisコマンド見ながら格闘した結果が下記です。

main.go

godotenvで.envを読んでいるのは、RedisのHost,Portを環境別に管理するイメージのコードにしたかったからで、infra.Redis()でClient生成して、RepositoryをNewしています。
あとはRepositoryのSetExGetHSetHGetAllを読んでその結果をfmt.Println(f)しているだけですね。

main.go
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)
	}
}

repository/cache_repository.go

go-redisの書き方とちょっと違ってcommandBuild形式?なので、最初は違和感ありましたが、最後にはスラスラ書けるようになってました。びっくり。

ただ、HSetで構造体を渡すUsecaseで、ループで一度にFieldValueに全フィールドと値をセットしてclient.Doするとなぜか怒られてしまったので、今回は1フィールドずつHSetする形にしています。
公式のなかに、おそらく有志が作ったと思われるGo-redis like API adapterがあったので、そちらも参考にしてみたのですがちょっと施行時間が足りずうまくいかなったので再チャレンジしたいですね。

repository/cache_repository.go
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

1
0
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?