// go-redis 外部 Redis 服务器实现 // 用于 Docker Compose / 生产环境部署 // Logan.Li package redis import ( "context" "fmt" "time" goredis "github.com/redis/go-redis/v9" ) // goRedisCache 基于 go-redis 的缓存实现 // Logan.Li type goRedisCache struct { client *goredis.Client } func newGoRedis(cfg Config) (Cache, error) { client := goredis.NewClient(&goredis.Options{ Addr: fmt.Sprintf("%s:%d", cfg.Host, cfg.Port), Password: cfg.Password, DB: cfg.DB, }) // 连接测试 ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() if err := client.Ping(ctx).Err(); err != nil { return nil, fmt.Errorf("redis: 连接失败 %s:%d: %w", cfg.Host, cfg.Port, err) } return &goRedisCache{client: client}, nil } func (r *goRedisCache) Capabilities() Capabilities { return Capabilities{SortedSet: true, PubSub: true} } func (r *goRedisCache) Get(ctx context.Context, key string) (string, error) { val, err := r.client.Get(ctx, key).Result() if err == goredis.Nil { return "", nil } return val, err } func (r *goRedisCache) Set(ctx context.Context, key string, value string, expiration time.Duration) error { return r.client.Set(ctx, key, value, expiration).Err() } // SetNX 原子 SET if Not eXists — go-redis 原生支持,用于分布式锁 func (r *goRedisCache) SetNX(ctx context.Context, key string, value string, expiration time.Duration) (bool, error) { return r.client.SetNX(ctx, key, value, expiration).Result() } func (r *goRedisCache) Del(ctx context.Context, keys ...string) error { return r.client.Del(ctx, keys...).Err() } func (r *goRedisCache) Exists(ctx context.Context, keys ...string) (int64, error) { return r.client.Exists(ctx, keys...).Result() } func (r *goRedisCache) Expire(ctx context.Context, key string, expiration time.Duration) error { return r.client.Expire(ctx, key, expiration).Err() } func (r *goRedisCache) Ping(ctx context.Context) error { return r.client.Ping(ctx).Err() } func (r *goRedisCache) Close() error { return r.client.Close() } func (r *goRedisCache) ZAdd(ctx context.Context, key string, member SortedSetMember) error { return r.client.ZAdd(ctx, key, goredis.Z{ Score: member.Score, Member: member.Member, }).Err() } func (r *goRedisCache) ZRangeByScore(ctx context.Context, key string, min, max string) ([]SortedSetMember, error) { zs, err := r.client.ZRangeByScoreWithScores(ctx, key, &goredis.ZRangeBy{ Min: min, Max: max, }).Result() if err != nil { return nil, err } members := make([]SortedSetMember, len(zs)) for i, z := range zs { members[i] = SortedSetMember{ Score: z.Score, Member: z.Member.(string), } } return members, nil } func (r *goRedisCache) ZRemRangeByScore(ctx context.Context, key string, min, max string) error { return r.client.ZRemRangeByScore(ctx, key, min, max).Err() } func (r *goRedisCache) Publish(ctx context.Context, channel string, message string) error { return r.client.Publish(ctx, channel, message).Err() }