package cache
import (
"context"
"encoding/json"
"fmt"
"log"
"sync"
"time"
"github.com/go-redis/redis/v8"
)
// 缓存接口
type Cache interface {
Get(ctx context.Context, key string) ([]byte, error)
Set(ctx context.Context, key string, value []byte, ttl time.Duration) error
Delete(ctx context.Context, key string) error
Exists(ctx context.Context, key string) (bool, error)
}
// 内存缓存实现
type MemoryCache struct {
data map[string]memoryCacheEntry
mu sync.RWMutex
}
type memoryCacheEntry struct {
Value []byte
ExpiresAt time.Time
}
func NewMemoryCache() *MemoryCache {
cache := &MemoryCache{
data: make(map[string]memoryCacheEntry),
}
// 启动清理goroutine
go cache.cleanup()
return cache
}
func (mc *MemoryCache) Get(ctx context.Context, key string) ([]byte, error) {
mc.mu.RLock()
defer mc.mu.RUnlock()
entry, exists := mc.data[key]
if !exists {
return nil, fmt.Errorf("key not found")
}
if time.Now().After(entry.ExpiresAt) {
return nil, fmt.Errorf("key expired")
}
return entry.Value, nil
}
func (mc *MemoryCache) Set(ctx context.Context, key string, value []byte, ttl time.Duration) error {
mc.mu.Lock()
defer mc.mu.Unlock()
mc.data[key] = memoryCacheEntry{
Value: value,
ExpiresAt: time.Now().Add(ttl),
}
return nil
}
func (mc *MemoryCache) Delete(ctx context.Context, key string) error {
mc.mu.Lock()
defer mc.mu.Unlock()
delete(mc.data, key)
return nil
}
func (mc *MemoryCache) Exists(ctx context.Context, key string) (bool, error) {
mc.mu.RLock()
defer mc.mu.RUnlock()
entry, exists := mc.data[key]
if !exists {
return false, nil
}
return !time.Now().After(entry.ExpiresAt), nil
}
func (mc *MemoryCache) cleanup() {
ticker := time.NewTicker(1 * time.Minute)
defer ticker.Stop()
for range ticker.C {
mc.mu.Lock()
now := time.Now()
for key, entry := range mc.data {
if now.After(entry.ExpiresAt) {
delete(mc.data, key)
}
}
mc.mu.Unlock()
}
}
// Redis缓存实现
type RedisCache struct {
client *redis.Client
}
func NewRedisCache(addr, password string, db int) *RedisCache {
client := redis.NewClient(&redis.Options{
Addr: addr,
Password: password,
DB: db,
})
return &RedisCache{
client: client,
}
}
func (rc *RedisCache) Get(ctx context.Context, key string) ([]byte, error) {
result, err := rc.client.Get(ctx, key).Result()
if err != nil {
return nil, err
}
return []byte(result), nil
}
func (rc *RedisCache) Set(ctx context.Context, key string, value []byte, ttl time.Duration) error {
return rc.client.Set(ctx, key, value, ttl).Err()
}
func (rc *RedisCache) Delete(ctx context.Context, key string) error {
return rc.client.Del(ctx, key).Err()
}
func (rc *RedisCache) Exists(ctx context.Context, key string) (bool, error) {
count, err := rc.client.Exists(ctx, key).Result()
return count > 0, err
}
// 多级缓存管理器
type MultiLevelCache struct {
l1Cache Cache // 内存缓存(L1)
l2Cache Cache // Redis缓存(L2)
l1TTL time.Duration
l2TTL time.Duration
}
func NewMultiLevelCache(l1Cache, l2Cache Cache, l1TTL, l2TTL time.Duration) *MultiLevelCache {
return &MultiLevelCache{
l1Cache: l1Cache,
l2Cache: l2Cache,
l1TTL: l1TTL,
l2TTL: l2TTL,
}
}
func (mlc *MultiLevelCache) Get(ctx context.Context, key string) ([]byte, error) {
// 先尝试L1缓存
if data, err := mlc.l1Cache.Get(ctx, key); err == nil {
return data, nil
}
// L1缓存未命中,尝试L2缓存
data, err := mlc.l2Cache.Get(ctx, key)
if err != nil {
return nil, err
}
// 将数据写入L1缓存
mlc.l1Cache.Set(ctx, key, data, mlc.l1TTL)
return data, nil
}
func (mlc *MultiLevelCache) Set(ctx context.Context, key string, value []byte) error {
// 同时写入L1和L2缓存
if err := mlc.l1Cache.Set(ctx, key, value, mlc.l1TTL); err != nil {
log.Printf("Failed to set L1 cache: %v", err)
}
return mlc.l2Cache.Set(ctx, key, value, mlc.l2TTL)
}
func (mlc *MultiLevelCache) Delete(ctx context.Context, key string) error {
// 同时删除L1和L2缓存
mlc.l1Cache.Delete(ctx, key)
return mlc.l2Cache.Delete(ctx, key)
}
// 缓存管理器
type CacheManager struct {
cache Cache
stats *CacheStats
mu sync.RWMutex
}
type CacheStats struct {
Hits int64
Misses int64
Sets int64
Deletes int64
}
func NewCacheManager(cache Cache) *CacheManager {
return &CacheManager{
cache: cache,
stats: &CacheStats{},
}
}
func (cm *CacheManager) Get(ctx context.Context, key string) ([]byte, error) {
data, err := cm.cache.Get(ctx, key)
cm.mu.Lock()
if err != nil {
cm.stats.Misses++
} else {
cm.stats.Hits++
}
cm.mu.Unlock()
return data, err
}
func (cm *CacheManager) Set(ctx context.Context, key string, value []byte, ttl time.Duration) error {
err := cm.cache.Set(ctx, key, value, ttl)
cm.mu.Lock()
cm.stats.Sets++
cm.mu.Unlock()
return err
}
func (cm *CacheManager) Delete(ctx context.Context, key string) error {
err := cm.cache.Delete(ctx, key)
cm.mu.Lock()
cm.stats.Deletes++
cm.mu.Unlock()
return err
}
func (cm *CacheManager) GetStats() CacheStats {
cm.mu.RLock()
defer cm.mu.RUnlock()
return *cm.stats
}
func (cm *CacheManager) HitRate() float64 {
cm.mu.RLock()
defer cm.mu.RUnlock()
total := cm.stats.Hits + cm.stats.Misses
if total == 0 {
return 0
}
return float64(cm.stats.Hits) / float64(total)
}
// 泛型缓存包装器
type TypedCache[T any] struct {
cache Cache
}
func NewTypedCache[T any](cache Cache) *TypedCache[T] {
return &TypedCache[T]{cache: cache}
}
func (tc *TypedCache[T]) Get(ctx context.Context, key string) (*T, error) {
data, err := tc.cache.Get(ctx, key)
if err != nil {
return nil, err
}
var result T
if err := json.Unmarshal(data, &result); err != nil {
return nil, fmt.Errorf("failed to unmarshal cached data: %w", err)
}
return &result, nil
}
func (tc *TypedCache[T]) Set(ctx context.Context, key string, value *T, ttl time.Duration) error {
data, err := json.Marshal(value)
if err != nil {
return fmt.Errorf("failed to marshal value: %w", err)
}
return tc.cache.Set(ctx, key, data, ttl)
}
func (tc *TypedCache[T]) Delete(ctx context.Context, key string) error {
return tc.cache.Delete(ctx, key)
}