Skip to content

Commit

Permalink
Merge pull request #176 from wubin1989/main
Browse files Browse the repository at this point in the history
Add cache layer to gorm, support mysql and postgresql
  • Loading branch information
wubin1989 authored Dec 17, 2023
2 parents 61c9fba + 77801e6 commit 62421a0
Show file tree
Hide file tree
Showing 29 changed files with 2,245 additions and 44 deletions.
2 changes: 1 addition & 1 deletion cmd/internal/svc/codegen/httphandlerimpl.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ var appendHttpHandlerImplTmpl = `
{{ $p.Name }} {{ $p.Type }}
{{- else if and (eq $m.HttpMethod "GET") (not (isBuiltin $p)) }}
{{ $p.Name }}Wrapper struct {
{{ $p.Name | title }} {{ $p.Type }} ` + "`" + `form:"{{ $p.Name }}"` + "`" + `
{{ $p.Name | title }} {{ $p.Type }} ` + "`" + `json:"{{ $p.Name }}"` + "`" + `
}
{{- else }}
{{ $p.Name }} {{ $p.Type }}
Expand Down
178 changes: 178 additions & 0 deletions framework/cache/cachemanager.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
package cache

import (
"strings"
"time"
"unsafe"

"github.com/dgraph-io/ristretto"
gocache "github.com/eko/gocache/lib/v4/cache"
"github.com/eko/gocache/lib/v4/metrics"
"github.com/eko/gocache/lib/v4/store"
go_cache_store "github.com/eko/gocache/store/go_cache/v4"
redis_store "github.com/eko/gocache/store/redis/v4"
ristretto_store "github.com/eko/gocache/store/ristretto/v4"
go_cache "github.com/patrickmn/go-cache"
"github.com/redis/go-redis/v9"

"github.com/unionj-cloud/go-doudou/v2/framework/config"
"github.com/unionj-cloud/go-doudou/v2/toolkit/cast"
"github.com/unionj-cloud/go-doudou/v2/toolkit/sliceutils"
"github.com/unionj-cloud/go-doudou/v2/toolkit/stringutils"
)

var CacheManager gocache.CacheInterface[any]

const (
// CacheStoreRistretto TODO
// There is a bug for CacheStoreRistretto, do not use it.
// Use CacheStoreGoCache or CacheStoreRedis or both.
CacheStoreRistretto = "ristretto"
CacheStoreGoCache = "go-cache"
CacheStoreRedis = "redis"
)

func init() {
conf := config.Config{
Cache: struct {
TTL int
Stores string
Redis struct {
Addr string
Username string
Password string
RouteByLatency bool `default:"true"`
RouteRandomly bool
}
Ristretto struct {
NumCounters int64 `default:"1000"`
MaxCost int64 `default:"100"`
BufferItems int64 `default:"64"`
}
GoCache struct {
Expiration time.Duration `default:"5m"`
CleanupInterval time.Duration `default:"10m"`
}
}{
TTL: cast.ToIntOrDefault(config.GddCacheTTL.Load(), config.DefaultGddCacheTTL),
Stores: config.GddCacheStores.LoadOrDefault(config.DefaultGddCacheStores),
Ristretto: struct {
NumCounters int64 `default:"1000"`
MaxCost int64 `default:"100"`
BufferItems int64 `default:"64"`
}{
NumCounters: cast.ToInt64OrDefault(config.GddCacheRistrettoNumCounters.Load(), config.DefaultGddCacheRistrettoNumCounters),
MaxCost: cast.ToInt64OrDefault(config.GddCacheRistrettoMaxCost.Load(), config.DefaultGddCacheRistrettoMaxCost),
BufferItems: cast.ToInt64OrDefault(config.GddCacheRistrettoBufferItems.Load(), config.DefaultGddCacheRistrettoBufferItems),
},
Redis: struct {
Addr string
Username string
Password string
RouteByLatency bool `default:"true"`
RouteRandomly bool
}{
Addr: config.GddCacheRedisAddr.LoadOrDefault(config.DefaultGddCacheRedisAddr),
Username: config.GddCacheRedisUser.LoadOrDefault(config.DefaultGddCacheRedisUser),
Password: config.GddCacheRedisPass.LoadOrDefault(config.DefaultGddCacheRedisPass),
RouteByLatency: cast.ToBoolOrDefault(config.GddCacheRedisRouteByLatency.Load(), config.DefaultGddCacheRedisRouteByLatency),
RouteRandomly: cast.ToBoolOrDefault(config.GddCacheRedisRouteRandomly.Load(), config.DefaultGddCacheRedisRouteRandomly),
},
GoCache: struct {
Expiration time.Duration `default:"5m"`
CleanupInterval time.Duration `default:"10m"`
}{
Expiration: config.GddCacheGocacheExpiration.LoadDurationOrDefault(config.DefaultGddCacheGocacheExpiration),
CleanupInterval: config.GddCacheGocacheCleanupInterval.LoadDurationOrDefault(config.DefaultGddCacheGocacheCleanupInterval),
},
},
Service: struct{ Name string }{
Name: config.GddServiceName.LoadOrDefault(config.DefaultGddServiceName),
},
}

CacheManager = NewCacheManager(conf)
}

func NewCacheManager(conf config.Config) gocache.CacheInterface[any] {
storesStr := conf.Cache.Stores
if stringutils.IsEmpty(storesStr) {
return nil
}
stores := strings.Split(storesStr, ",")

var setterCaches []gocache.SetterCacheInterface[any]
ttl := conf.Cache.TTL

if sliceutils.StringContains(stores, CacheStoreRistretto) {
ristrettoCache, err := ristretto.NewCache(&ristretto.Config{
NumCounters: conf.Cache.Ristretto.NumCounters,
MaxCost: conf.Cache.Ristretto.MaxCost,
BufferItems: conf.Cache.Ristretto.BufferItems,
Cost: func(value interface{}) int64 {
return int64(unsafe.Sizeof(value))
},
})
if err != nil {
panic(err)
}
var ristrettoStore *ristretto_store.RistrettoStore
if ttl > 0 {
ristrettoStore = ristretto_store.NewRistretto(ristrettoCache, store.WithExpiration(time.Duration(ttl)*time.Second), store.WithSynchronousSet())
} else {
ristrettoStore = ristretto_store.NewRistretto(ristrettoCache, store.WithSynchronousSet())
}
setterCaches = append(setterCaches, gocache.New[any](ristrettoStore))
}

if sliceutils.StringContains(stores, CacheStoreGoCache) {
gocacheClient := go_cache.New(conf.Cache.GoCache.Expiration, conf.Cache.GoCache.CleanupInterval)
setterCaches = append(setterCaches, gocache.New[any](go_cache_store.NewGoCache(gocacheClient)))
}

if sliceutils.StringContains(stores, CacheStoreRedis) {
redisAddr := conf.Cache.Redis.Addr
if stringutils.IsNotEmpty(redisAddr) {
addrs := strings.Split(redisAddr, ",")
var redisClient redis_store.RedisClientInterface
if len(addrs) > 1 {
redisClient = redis.NewClusterClient(&redis.ClusterOptions{
Addrs: addrs,
Username: conf.Cache.Redis.Username,
Password: conf.Cache.Redis.Password,
RouteByLatency: conf.Cache.Redis.RouteByLatency,
RouteRandomly: conf.Cache.Redis.RouteRandomly,
})
} else {
redisClient = redis.NewClient(&redis.Options{
Addr: addrs[0],
Username: conf.Cache.Redis.Username,
Password: conf.Cache.Redis.Password,
})
}
var redisStore *redis_store.RedisStore
if ttl > 0 {
redisStore = redis_store.NewRedis(redisClient, store.WithExpiration(time.Duration(ttl)*time.Second))
} else {
redisStore = redis_store.NewRedis(redisClient)
}
setterCaches = append(setterCaches, gocache.New[any](redisStore))
}
}

var cacheManager gocache.CacheInterface[any]

// Initialize chained cache
cacheManager = gocache.NewChain[any](setterCaches...)

serviceName := conf.Service.Name
if stringutils.IsNotEmpty(serviceName) {
// Initializes Prometheus metrics service
promMetrics := metrics.NewPrometheus(serviceName)

// Initialize chained cache
cacheManager = gocache.NewMetric[any](promMetrics, cacheManager)
}

return cacheManager
}
85 changes: 76 additions & 9 deletions framework/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,30 @@ package config

import (
"fmt"
"net"
"net/url"
"os"
"strconv"
"strings"
"sync"
"time"

"github.com/apolloconfig/agollo/v4"
"github.com/apolloconfig/agollo/v4/env/config"
"github.com/pkg/errors"
"github.com/rs/zerolog"
"github.com/sirupsen/logrus"
"github.com/wubin1989/nacos-sdk-go/v2/common/constant"
"github.com/wubin1989/nacos-sdk-go/v2/vo"
_ "go.uber.org/automaxprocs"

"github.com/unionj-cloud/go-doudou/v2/framework"
"github.com/unionj-cloud/go-doudou/v2/framework/configmgr"
"github.com/unionj-cloud/go-doudou/v2/toolkit/cast"
"github.com/unionj-cloud/go-doudou/v2/toolkit/dotenv"
"github.com/unionj-cloud/go-doudou/v2/toolkit/stringutils"
"github.com/unionj-cloud/go-doudou/v2/toolkit/yaml"
"github.com/unionj-cloud/go-doudou/v2/toolkit/zlogger"
"github.com/wubin1989/nacos-sdk-go/v2/common/constant"
"github.com/wubin1989/nacos-sdk-go/v2/vo"
_ "go.uber.org/automaxprocs"
"net"
"net/url"
"os"
"strconv"
"strings"
"sync"
)

func LoadConfigFromLocal() {
Expand Down Expand Up @@ -280,6 +283,27 @@ const (
GddDBPostgresPreferSimpleProtocol envVariable = "GDD_DB_POSTGRES_PREFERSIMPLEPROTOCOL"
GddDBPostgresWithoutReturning envVariable = "GDD_DB_POSTGRES_WITHOUTRETURNING"

GddDbPrometheusEnable envVariable = "GDD_DB_PROMETHEUS_ENABLE"
GddDbPrometheusRefreshInterval envVariable = "GDD_DB_PROMETHEUS_REFRESHINTERVAL"
GddDbPrometheusDBName envVariable = "GDD_DB_PROMETHEUS_DBNAME"

GddDbCacheEnable envVariable = "GDD_DB_CACHE_ENABLE"
GddCacheTTL envVariable = "GDD_CACHE_TTL"
GddCacheStores envVariable = "GDD_CACHE_STORES"

GddCacheRedisAddr envVariable = "GDD_CACHE_REDIS_ADDR"
GddCacheRedisUser envVariable = "GDD_CACHE_REDIS_USER"
GddCacheRedisPass envVariable = "GDD_CACHE_REDIS_PASS"
GddCacheRedisRouteByLatency envVariable = "GDD_CACHE_REDIS_ROUTEBYLATENCY"
GddCacheRedisRouteRandomly envVariable = "GDD_CACHE_REDIS_ROUTERANDOMLY"

GddCacheRistrettoNumCounters envVariable = "GDD_CACHE_RISTRETTO_NUMCOUNTERS"
GddCacheRistrettoMaxCost envVariable = "GDD_CACHE_RISTRETTO_MAXCOST"
GddCacheRistrettoBufferItems envVariable = "GDD_CACHE_RISTRETTO_BUFFERITEMS"

GddCacheGocacheExpiration envVariable = "GDD_CACHE_GOCACHE_EXPIRATION"
GddCacheGocacheCleanupInterval envVariable = "GDD_CACHE_GOCACHE_CLEANUP_INTERVAL"

GddZkServers envVariable = "GDD_ZK_SERVERS"
GddZkSequence envVariable = "GDD_ZK_SEQUENCE"
GddZkDirectoryPattern envVariable = "GDD_ZK_DIRECTORY_PATTERN"
Expand All @@ -298,6 +322,18 @@ func (receiver envVariable) LoadOrDefault(d string) string {
return val
}

func (receiver envVariable) LoadDurationOrDefault(d string) time.Duration {
val, _ := time.ParseDuration(d)
if stringutils.IsNotEmpty(receiver.Load()) {
var err error
val, err = time.ParseDuration(receiver.Load())
if err != nil {
panic(err)
}
}
return val
}

// String return string representation for receiver
func (receiver envVariable) String() string {
return receiver.Load()
Expand Down Expand Up @@ -456,5 +492,36 @@ type Config struct {
ConnMaxLifetime string
ConnMaxIdleTime string
}
Cache struct {
Enable bool
}
Prometheus struct {
Enable bool
RefreshInterval int `default:"15"`
DBName string
}
}
Cache struct {
TTL int
Stores string
Redis struct {
Addr string
Username string
Password string
RouteByLatency bool `default:"true"`
RouteRandomly bool
}
Ristretto struct {
NumCounters int64 `default:"1000"`
MaxCost int64 `default:"100"`
BufferItems int64 `default:"64"`
}
GoCache struct {
Expiration time.Duration `default:"5m"`
CleanupInterval time.Duration `default:"10m"`
}
}
Service struct {
Name string
}
}
23 changes: 22 additions & 1 deletion framework/config/default.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package config

import (
"github.com/unionj-cloud/go-doudou/v2/framework/configmgr"
"gorm.io/gorm/logger"

"github.com/unionj-cloud/go-doudou/v2/framework/configmgr"
)

const FrameworkName = "go-doudou"
Expand Down Expand Up @@ -128,4 +129,24 @@ const (
DefaultGddZkServers = ""
DefaultGddZkSequence = false
DefaultGddZkDirectoryPattern = "/registry/%s/providers"

DefaultGddDbPrometheusEnable = false
DefaultGddDbPrometheusRefreshInterval = 15
DefaultGddDbPrometheusDBName = ""

DefaultGddDbCacheEnable = false
DefaultGddCacheRedisAddr = ""
DefaultGddCacheRedisUser = ""
DefaultGddCacheRedisPass = ""
DefaultGddCacheRedisRouteByLatency = true
DefaultGddCacheRedisRouteRandomly = false
DefaultGddCacheTTL = 0
DefaultGddCacheStores = ""

DefaultGddCacheRistrettoNumCounters = 1000
DefaultGddCacheRistrettoMaxCost = 100
DefaultGddCacheRistrettoBufferItems = 64

DefaultGddCacheGocacheExpiration = "5m"
DefaultGddCacheGocacheCleanupInterval = "10m"
)
48 changes: 48 additions & 0 deletions framework/database/cacheradapter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package database

import (
"context"
"github.com/eko/gocache/lib/v4/cache"
"github.com/eko/gocache/lib/v4/store"
"github.com/pkg/errors"
"github.com/unionj-cloud/go-doudou/v2/toolkit/caches"
"github.com/unionj-cloud/go-doudou/v2/toolkit/zlogger"
)

var _ caches.Cacher = (*CacherAdapter)(nil)

type CacherAdapter struct {
marshaler *Marshaler
}

func (c *CacherAdapter) Delete(tag string, tags ...string) error {
invalidateTags := []string{tag}
invalidateTags = append(invalidateTags, tags...)
if err := c.marshaler.Invalidate(context.Background(), store.WithInvalidateTags(invalidateTags)); err != nil {
return errors.WithStack(err)
}
return nil
}

func (c *CacherAdapter) Get(key string) *caches.Query {
result := new(caches.Query)
_, err := c.marshaler.Get(context.Background(), key, result)
if err != nil {
zlogger.Err(err).Msg(err.Error())
return nil
}
return result
}

func (c *CacherAdapter) Store(key string, val *caches.Query) error {
if err := c.marshaler.Set(context.Background(), key, val, store.WithTags(val.Tags)); err != nil {
return errors.WithStack(err)
}
return nil
}

func NewCacherAdapter(cacheManager cache.CacheInterface[any]) *CacherAdapter {
return &CacherAdapter{
marshaler: NewMarshaler(cacheManager),
}
}
Loading

0 comments on commit 62421a0

Please sign in to comment.