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の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)
}
}
repository/cache_repository.go
go-redisの書き方とちょっと違ってcommandBuild形式?なので、最初は違和感ありましたが、最後にはスラスラ書けるようになってました。びっくり。
ただ、HSetで構造体を渡すUsecaseで、ループで一度にFieldValue
に全フィールドと値をセットしてclient.Doするとなぜか怒られてしまったので、今回は1フィールドずつHSetする形にしています。
公式のなかに、おそらく有志が作ったと思われるGo-redis like API adapter
があったので、そちらも参考にしてみたのですがちょっと施行時間が足りずうまくいかなったので再チャレンジしたいですね。
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