11 KiB
11 KiB
缓存模块设计
基于Clean Architecture设计,支持内存缓存和Redis缓存两种实现方式,提供统一的缓存接口,便于在不同环境下切换。
设计理念
- 抽象接口:通过接口抽象不同缓存实现
- 配置驱动:支持运行时配置缓存类型和参数
- 优雅降级:缓存异常时不影响业务逻辑
- 性能监控:提供缓存命中率等指标
- 序列化支持:自动处理常见数据类型的序列化
核心架构
1. 缓存接口设计
// Cache 缓存接口
type Cache interface {
// 基础操作
Get(ctx context.Context, key string) (string, error)
Set(ctx context.Context, key string, value string, ttl time.Duration) error
Del(ctx context.Context, keys ...string) error
Exists(ctx context.Context, key string) (bool, error)
// TTL操作
TTL(ctx context.Context, key string) (time.Duration, error)
Expire(ctx context.Context, key string, ttl time.Duration) error
// 数值操作
Incr(ctx context.Context, key string) (int64, error)
Decr(ctx context.Context, key string) (int64, error)
// 批量操作
MGet(ctx context.Context, keys ...string) ([]string, error)
MSet(ctx context.Context, pairs map[string]string, ttl time.Duration) error
// 工具方法
Ping(ctx context.Context) error
Close() error
}
// CacheWithTTL 带TTL的缓存接口
type CacheWithTTL interface {
Cache
GetWithTTL(ctx context.Context, key string) (string, time.Duration, error)
}
// Serializer 序列化接口
type Serializer interface {
Marshal(v interface{}) ([]byte, error)
Unmarshal(data []byte, v interface{}) error
}
2. 缓存配置
// CacheConfig 缓存配置
type CacheConfig struct {
Type string `mapstructure:"type"` // 缓存类型: memory, redis
TTL time.Duration `mapstructure:"ttl"` // 默认TTL
Enabled bool `mapstructure:"enabled"` // 是否启用缓存
Redis RedisConfig `mapstructure:"redis"` // Redis配置
Memory MemoryConfig `mapstructure:"memory"` // 内存配置
}
// RedisConfig Redis配置
type RedisConfig struct {
Host string `mapstructure:"host"`
Port int `mapstructure:"port"`
Password string `mapstructure:"password"`
DB int `mapstructure:"db"`
PoolSize int `mapstructure:"pool_size"`
MinIdleConns int `mapstructure:"min_idle_conns"`
ConnMaxIdleTime time.Duration `mapstructure:"conn_max_idle_time"`
DialTimeout time.Duration `mapstructure:"dial_timeout"`
ReadTimeout time.Duration `mapstructure:"read_timeout"`
WriteTimeout time.Duration `mapstructure:"write_timeout"`
}
// MemoryConfig 内存配置
type MemoryConfig struct {
MaxSize int `mapstructure:"max_size"` // 最大缓存条目数
CleanupInterval time.Duration `mapstructure:"cleanup_interval"` // 清理间隔
}
实现方案
1. 内存缓存实现
// memoryCache 内存缓存实现
type memoryCache struct {
data sync.Map
ttl sync.Map
maxSize int
mu sync.RWMutex
}
// NewMemoryCache 创建内存缓存
func NewMemoryCache(config MemoryConfig) Cache {
cache := &memoryCache{
maxSize: config.MaxSize,
data: sync.Map{},
ttl: sync.Map{},
}
// 启动清理协程
if config.CleanupInterval > 0 {
go cache.cleanup(config.CleanupInterval)
}
return cache
}
func (m *memoryCache) Get(ctx context.Context, key string) (string, error) {
value, ok := m.data.Load(key)
if !ok {
return "", ErrCacheMiss
}
// 检查TTL
if ttl, exists := m.ttl.Load(key); exists {
if time.Now().After(ttl.(time.Time)) {
m.data.Delete(key)
m.ttl.Delete(key)
return "", ErrCacheMiss
}
}
return value.(string), nil
}
func (m *memoryCache) Set(ctx context.Context, key string, value string, ttl time.Duration) error {
// 简单的容量检查(生产环境建议使用更完善的LRU缓存库)
if m.maxSize > 0 {
// 这里可以实现简单的容量控制逻辑
// 生产环境建议使用 github.com/hashicorp/golang-lru 等专业库
}
m.data.Store(key, value)
if ttl > 0 {
m.ttl.Store(key, time.Now().Add(ttl))
}
return nil
}
2. Redis缓存实现
// redisCache Redis缓存实现
type redisCache struct {
client *redis.Client
ttl time.Duration
}
// NewRedisCache 创建Redis缓存
func NewRedisCache(config RedisConfig, defaultTTL time.Duration) (Cache, error) {
client := redis.NewClient(&redis.Options{
Addr: fmt.Sprintf("%s:%d", config.Host, config.Port),
Password: config.Password,
DB: config.DB,
PoolSize: config.PoolSize,
})
// 测试连接
if err := client.Ping(context.Background()).Err(); err != nil {
return nil, fmt.Errorf("failed to connect to Redis: %w", err)
}
return &redisCache{
client: client,
ttl: defaultTTL,
}, nil
}
func (r *redisCache) Get(ctx context.Context, key string) (string, error) {
value, err := r.client.Get(ctx, key).Result()
if err == redis.Nil {
return "", ErrCacheMiss
}
if err != nil {
return "", fmt.Errorf("cache get error: %w", err)
}
return value, nil
}
func (r *redisCache) Set(ctx context.Context, key string, value string, ttl time.Duration) error {
if ttl == 0 {
ttl = r.ttl
}
return r.client.Set(ctx, key, value, ttl).Err()
}
3. 缓存工厂
// NewCache 创建缓存实例
func NewCache(config CacheConfig) (Cache, error) {
if !config.Enabled {
return &noOpCache{}, nil
}
switch config.Type {
case "memory":
return NewMemoryCache(config.Memory)
case "redis":
return NewRedisCache(config.Redis, config.TTL)
default:
return nil, fmt.Errorf("unsupported cache type: %s", config.Type)
}
}
// noOpCache 空操作缓存(当缓存被禁用时使用)
type noOpCache struct{}
func (n *noOpCache) Get(ctx context.Context, key string) (string, error) {
return "", ErrCacheMiss
}
func (n *noOpCache) Set(ctx context.Context, key string, value string, ttl time.Duration) error {
return nil
}
// ... 其他方法都返回nil或默认值
高级功能
1. 序列化支持
// JSONSerializer JSON序列化器
type JSONSerializer struct{}
func (j *JSONSerializer) Marshal(v interface{}) ([]byte, error) {
return json.Marshal(v)
}
func (j *JSONSerializer) Unmarshal(data []byte, v interface{}) error {
return json.Unmarshal(data, v)
}
// Cached 带序列化的缓存包装器
type Cached struct {
Cache
Serializer Serializer
}
func (c *Cached) SetObject(ctx context.Context, key string, value interface{}, ttl time.Duration) error {
data, err := c.Serializer.Marshal(value)
if err != nil {
return err
}
return c.Cache.Set(ctx, key, string(data), ttl)
}
func (c *Cached) GetObject(ctx context.Context, key string, dest interface{}) error {
value, err := c.Cache.Get(ctx, key)
if err != nil {
return err
}
return c.Serializer.Unmarshal([]byte(value), dest)
}
2. 缓存装饰器
// CacheDecorator 缓存装饰器
type CacheDecorator struct {
Cache
prefix string // 键前缀
}
func (c *CacheDecorator) buildKey(key string) string {
if c.prefix != "" {
return c.prefix + ":" + key
}
return key
}
func (c *CacheDecorator) Get(ctx context.Context, key string) (string, error) {
return c.Cache.Get(ctx, c.buildKey(key))
}
func (c *CacheDecorator) Set(ctx context.Context, key string, value string, ttl time.Duration) error {
return c.Cache.Set(ctx, c.buildKey(key), value, ttl)
}
3. 缓存键管理
// KeyBuilder 缓存键构建器
type KeyBuilder struct {
prefix string
separator string
}
func NewKeyBuilder(prefix string) *KeyBuilder {
return &KeyBuilder{
prefix: prefix,
separator: ":",
}
}
func (k *KeyBuilder) Build(parts ...string) string {
if k.prefix != "" {
parts = append([]string{k.prefix}, parts...)
}
return strings.Join(parts, k.separator)
}
// 预定义键模式
const (
UserKeyPrefix = "user"
SessionKeyPrefix = "session"
SMSCodePrefix = "sms:code"
SMSLimitPrefix = "sms:limit"
)
// 使用示例
keyBuilder := NewKeyBuilder("app")
userKey := keyBuilder.Build(UserKeyPrefix, "123") // "app:user:123"
配置示例
内存缓存配置
cache:
enabled: true
type: memory
ttl: 30m
memory:
max_size: 10000
cleanup_interval: 10m
Redis缓存配置
cache:
enabled: true
type: redis
ttl: 30m
redis:
host: localhost
port: 6379
password: ""
db: 0
pool_size: 10
min_idle_conns: 5
conn_max_idle_time: 30m
dial_timeout: 5s
read_timeout: 3s
write_timeout: 3s
错误处理
var (
ErrCacheMiss = errors.New("cache miss")
ErrCacheClosed = errors.New("cache closed")
)
// 优雅降级:缓存错误不影响业务
func safeCacheOperation(cache Cache, operation func() error) error {
defer func() {
if r := recover(); r != nil {
// 记录错误日志,但不影响业务
}
}()
if cache == nil {
return nil // 缓存未启用,直接跳过
}
return operation()
}
监控指标
// CacheStats 缓存统计信息
type CacheStats struct {
Hits int64 // 命中次数
Misses int64 // 未命中次数
Errors int64 // 错误次数
HitRate float64 // 命中率
TotalOps int64 // 总操作数
AvgRespTime int64 // 平均响应时间(纳秒)
}
func (s *CacheStats) RecordHit() {
s.Hits++
s.updateHitRate()
}
func (s *CacheStats) RecordMiss() {
s.Misses++
s.updateHitRate()
}
func (s *CacheStats) RecordError() {
s.Errors++
}
func (s *CacheStats) updateHitRate() {
total := s.Hits + s.Misses
if total > 0 {
s.HitRate = float64(s.Hits) / float64(total)
}
}
使用示例
// 初始化缓存
cache, err := NewCache(config.Cache)
if err != nil {
log.Fatal(err)
}
// 基础使用
cache.Set(ctx, "user:123", "user_data", time.Hour)
data, err := cache.Get(ctx, "user:123")
// 带序列化的使用
cached := &Cached{
Cache: cache,
Serializer: &JSONSerializer{},
}
user := User{ID: 123, Name: "John"}
cached.SetObject(ctx, "user:123", user, time.Hour)
var userData User
cached.GetObject(ctx, "user:123", &userData)
实施计划
第一阶段:核心功能 (P0)
- 实现缓存接口和基础实现类
- 支持内存缓存 (sync.Map)
- 支持Redis缓存 (go-redis/v9)
- 实现缓存工厂和配置加载
- 集成到现有项目中替换现有缓存逻辑
第二阶段:增强功能 (P1)
- 添加序列化支持 (JSONSerializer)
- 实现缓存键构建器 (KeyBuilder)
- 添加TTL查询和过期时间管理
- 完善批量操作支持
第三阶段:监控运维 (P2)
- 实现缓存统计和监控指标
- 添加性能监控和告警
- 优化连接池和错误重试
- 支持缓存预热和配置热更新