Files
xlgo-core/cache/cache.go
T
杭州明婳科技 0c9f256dfc feat(v1.1.0): HA & Manager 化
对应体检报告 #10-#24,一次性完成 v1.1.0 全部 13 项:

- #10 storage/cache/redis/jwt/logger 五组件 Manager 化(XxxManager + DefaultXxx + SetDefaultXxxManager,包级 facade 保留兼容)
- #11 删除 wire 包 + 清理 StartServerWithPort/GracefulShutdown 双轨
- #12 Lifecycle Hooks(OnInit/OnStart/OnReady/OnStop)
- #13 Server 参数配置化(timeout/TLS/unix_socket/response_mode)
- #14 JWTConfig.Expire 改 time.Duration + 删 AppConfig.TokenExpire
- #15 response Mode 开关(ModeBusiness/ModeREST)
- #16 Config.Validate 启动期校验
- #17 livez/readyz 探针路由
- #18 Prometheus metrics 中间件 + /metrics
- #19 请求级 Timeout 中间件
- #21 主库探活 + replica 健康剔除
- #22 App.Go + in-flight goroutine 管理
- #24 RequestID 默认装入 + #23 核对

Breaking:删 wire、删 TokenExpire、JWTConfig.Expire 类型变更、删双轨函数。
详见 CHANGELOG 升级说明。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 10:41:05 +08:00

205 lines
4.8 KiB
Go

package cache
import (
"context"
"encoding/json"
"sync"
"time"
"github.com/EthanCodeCraft/xlgo-core/logger"
"github.com/EthanCodeCraft/xlgo-core/database"
"github.com/redis/go-redis/v9"
"go.uber.org/zap"
)
// CacheService 缓存服务接口
type CacheService interface {
// Get 获取缓存值,如果存在则反序列化到 dest 并返回 true
Get(ctx context.Context, key string, dest any) bool
// Set 设置缓存值
Set(ctx context.Context, key string, value any, ttl time.Duration) error
// Delete 删除缓存
Delete(ctx context.Context, key string) error
// DeleteByPattern 按模式删除缓存
DeleteByPattern(ctx context.Context, pattern string) error
// Exists 检查缓存是否存在
Exists(ctx context.Context, key string) bool
}
// redisCache Redis 缓存实现
type redisCache struct {
client *redis.Client
}
// NewRedisCache 创建 Redis 缓存实例
func NewRedisCache() CacheService {
return &redisCache{
client: database.GetRedis(),
}
}
// Get 获取缓存值
func (c *redisCache) Get(ctx context.Context, key string, dest any) bool {
if c.client == nil {
return false
}
val, err := c.client.Get(ctx, key).Result()
if err != nil {
if err != redis.Nil {
logger.Warn("缓存获取失败", zap.String("key", key), zap.Error(err))
}
return false
}
if err := json.Unmarshal([]byte(val), dest); err != nil {
logger.Warn("缓存反序列化失败", zap.String("key", key), zap.Error(err))
return false
}
return true
}
// Set 设置缓存值
func (c *redisCache) Set(ctx context.Context, key string, value any, ttl time.Duration) error {
if c.client == nil {
return nil // Redis 未启用,跳过缓存
}
data, err := json.Marshal(value)
if err != nil {
logger.Warn("缓存序列化失败", zap.String("key", key), zap.Error(err))
return err
}
if err := c.client.Set(ctx, key, data, ttl).Err(); err != nil {
logger.Warn("缓存设置失败", zap.String("key", key), zap.Error(err))
return err
}
return nil
}
// Delete 删除缓存
func (c *redisCache) Delete(ctx context.Context, key string) error {
if c.client == nil {
return nil
}
if err := c.client.Del(ctx, key).Err(); err != nil {
logger.Warn("缓存删除失败", zap.String("key", key), zap.Error(err))
return err
}
return nil
}
// DeleteByPattern 按模式删除缓存(使用 SCAN 避免阻塞 Redis)
func (c *redisCache) DeleteByPattern(ctx context.Context, pattern string) error {
if c.client == nil {
return nil
}
var cursor uint64
var deleted int
for {
// 使用 SCAN 命令迭代查找匹配的键
keys, nextCursor, err := c.client.Scan(ctx, cursor, pattern, 100).Result()
if err != nil {
logger.Warn("缓存键扫描失败", zap.String("pattern", pattern), zap.Error(err))
return err
}
// 删除找到的键
if len(keys) > 0 {
if err := c.client.Del(ctx, keys...).Err(); err != nil {
logger.Warn("缓存批量删除失败", zap.Strings("keys", keys), zap.Error(err))
return err
}
deleted += len(keys)
}
// 更新游标
cursor = nextCursor
// 游标为 0 表示遍历完成
if cursor == 0 {
break
}
}
if deleted > 0 {
logger.Debug("缓存批量删除完成", zap.String("pattern", pattern), zap.Int("count", deleted))
}
return nil
}
// Exists 检查缓存是否存在
func (c *redisCache) Exists(ctx context.Context, key string) bool {
if c.client == nil {
return false
}
return c.client.Exists(ctx, key).Val() > 0
}
// CacheManager 缓存管理器(#10)。照 database.Manager 模式:
// 实例化 + DefaultCache 全局默认 + 包级 facade 代理,支持测试注入 mock 实现。
type CacheManager struct {
mu sync.Mutex
svc CacheService
}
// DefaultCache 默认缓存管理器,包级 facade 代理到它。
var DefaultCache = NewCacheManager()
// NewCacheManager 创建缓存管理器实例。
func NewCacheManager() *CacheManager { return &CacheManager{} }
// SetDefaultCacheManager 提升指定 CacheManager 为全局默认。
func SetDefaultCacheManager(m *CacheManager) {
if m != nil {
DefaultCache = m
}
}
// Init 初始化缓存服务(基于 DefaultRedis 的客户端)。
func (m *CacheManager) Init() {
m.mu.Lock()
defer m.mu.Unlock()
m.svc = NewRedisCache()
}
// Set 设置缓存服务实现(用于注入 mock 或自定义实现)。
func (m *CacheManager) Set(svc CacheService) {
m.mu.Lock()
defer m.mu.Unlock()
m.svc = svc
}
// Get 返回缓存服务(未初始化时延迟初始化)。
func (m *CacheManager) Get() CacheService {
m.mu.Lock()
defer m.mu.Unlock()
if m.svc == nil {
m.svc = NewRedisCache()
}
return m.svc
}
// --- 包级 facade(代理到 DefaultCache,兼容存量) ---
// Init 初始化全局缓存实例
func Init() {
DefaultCache.Init()
}
// GetCache 获取全局缓存实例
func GetCache() CacheService {
return DefaultCache.Get()
}