0
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?

Go (Gin Web)でサーバーキャッシュを扱う

Posted at

Go (Gin Web)でサーバーキャッシュを扱う

Goでサーバーキャッシュを扱うときの方法を簡単にまとめます。
大規模のトラフィックを捌くときにお役に立てばと思います。
フレームワークはGin Webを利用する想定です。

main.goでキャッシュの接続を記述

main.go
package main

import (
	"os"
	"time"
	"tkol-lite/controllers"
	"tkol-lite/models"
	"tkol-lite/services"

	"github.com/gin-contrib/cors"
	"github.com/gin-gonic/gin"
)

func main() {

	os.Setenv("TZ", "UTC") // タイムゾーンの設定
	r := gin.Default()

	r.Use(cors.New(cors.Config{

		AllowOrigins: []string{ // アクセスを許可したいアクセス元
			"http://example.com",
		},

		AllowMethods: []string{ // アクセスを許可したいHTTPメソッド
			"POST",
			"GET",
			"PUT",
			"DELETE",
		},

		AllowHeaders: []string{ // 許可したいHTTPリクエストヘッダ
			"Access-Control-Allow-Credentials",
			"Access-Control-Allow-Headers",
			"Content-Type",
			"Content-Length",
			"Accept-Encoding",
			"Authorization",
		},

		AllowCredentials: true, // cookieなどの情報を必要とするかどうか

		MaxAge: 24 * time.Hour, // preflightリクエストの結果をキャッシュする時間
	}))

	models.ConnectDatabase() // DB初期化
	models.CreateCache()     // キャッシュ初期化
    
    // (中略)
    r.GET("/article/:id", controllers.FetchArticleById)
    // (中略)
    
	r.Run()
}

Controllerからキャッシュの読み込み、書き込み

キャッシュに記憶するデータをユニークのキーで管理するようにし、該当キャッシュが存在する場合はDBへのアクセスより、キャッシュから取り出すことを優先する

controllers/article.go
package controllers

import (
	"fmt"
	"net/http"
	"strconv"
	"tkol-lite/models"
	"tkol-lite/services"

	"github.com/gin-gonic/gin"
)

func FetchArticleById(c *gin.Context) { // アイテムのIDを指定して取得
	cache_key := "article_" + c.Param("id")
	article, cache_err := models.GetCache(cache_key) // キャッシュがあれば、キャッシュから取得
	if cache_err != nil { // キャッシュ存在しない場合、DBからデータを取得して、さらにキャッシュに保存
		article, _ = services.FetchArticleById(c.Param("id"))
		models.SetCache(cache_key, article)
	}
	c.JSON(http.StatusOK, gin.H{"data": article})
}

キャッシュのベースファイル

インメモリキャッシュを使う場合

個人的に使うケースはないと思いますが、簡易のテスト実行するときにいいかもしれません。
インメモリキャッシュですので、もちろんRedisなどよりはパフォーマンスはよいですが、そもそも大量のトラフィックを捌くために、サーバーが複数台にわけることとなるため、実際のところ、実用性はないです。

models/cache.go
package models

import (
	"errors"
	"time"

	"github.com/patrickmn/go-cache"
)

var go_cache *cache.Cache

func CreateCache() {
	c := cache.New(30*time.Minute, 60*time.Minute)
	go_cache = c
}

func SetCache(k string, v interface{}) { // キャッシュを保存
	go_cache.Set(k, v, 30*time.Second) // 保存期間を指定
}

func GetCache(k string) (interface{}, error) { // キャッシュを取得
	err := errors.New("no cache")
	r, ok := go_cache.Get(k)
	if ok {
		err = nil
	}
	return r, err
}

func DeleteCache(k string) { // キャッシュを削除
	go_cache.Delete(k)
}

Redisなどのキャッシュストレージを使う場合

複数台のサーバーを利用すると、インメモリキャッシュがほとんど無意味、というよりも返ってカオスになってしまいますので、Redisなどのキャッシュストレージが必要となります。
Docker環境で試したところ、インメモリキャッシュの速度の1/3となります(当たり前ですが)。

models/cache.go
package models

import (
	"time"

	"context"
	"encoding/json"
	"os"
	"github.com/joho/godotenv"
	"github.com/redis/go-redis/v9"
)

var go_cache *redis.Client
var ctx = context.Background()

func CreateCache() {
	godotenv.Load(".env") // 環境ファイルを読み込み

	go_cache = redis.NewClient(&redis.Options{
		Addr:     os.Getenv("CACHE"), // redisの接続先
		Password: "",                 // パスワード
		DB:       0,                  // デフォルトDBを使う
		PoolSize: 1000,               // サイズの上限
	})
	if err := go_cache.Ping(ctx).Err(); err != nil {
		panic(err)
	}
	go_cache.FlushAll(ctx)
}

func SetCache(k string, v interface{}) (interface{}, error) { // キャッシュを保存
	data, err := json.Marshal(v) // オブジェクト式をラッパーしたjson形式へ変換
	result, err := go_cache.Set(ctx, k, data, 0).Result()
	go_cache.Expire(ctx, k, 30*time.Second) // 保存期間を指定
	return result, err
}

func GetCache(k string) (interface{}, error) { // キャッシュを取得
	var data interface{}
	result, err := go_cache.Get(ctx, k).Result()
	json.Unmarshal([]byte(result), &data) // キャッシュに保存されたjson形式へまたオブジェクトへ変換
	return data, err
}

func DeleteCache(k string) (interface{}, error) { // キャッシュを削除
	result, err := go_cache.Del(ctx, k).Result()
	return result, err
}

0
0
0

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
0
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?