2025-06-13 17:23:16 +08:00

87 lines
2.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package session
import (
"context"
"errors"
"time"
"github.com/redis/go-redis/v9"
)
// 为所有 Redis 操作定义一个合理的超时时间。
// 这个值应该根据你的服务SLA和网络状况来定通常在50-500毫秒之间是比较合理的。
// 最佳实践:这个值应该来自配置文件,而不是硬编码。
const redisTimeout = 200 * time.Millisecond
// RedisStore 表示一个使用 go-redis/v9 客户端的 scs.Store 实现。
type RedisStore struct {
// 内嵌 go-redis 客户端
client *redis.Client
}
// NewRedisStore 是 RedisStore 的构造函数。
func NewRedisStore(client *redis.Client) *RedisStore {
return &RedisStore{
client: client,
}
}
// Find 方法根据 session token 从 Redis 中查找 session 数据。
// 如果 token 不存在或已过期exists 返回 false。
func (s *RedisStore) Find(token string) ([]byte, bool, error) {
// ✅ 最佳实践: 为数据库操作创建带超时的上下文
ctx, cancel := context.WithTimeout(context.Background(), redisTimeout)
// ✅ 必须: 无论函数如何返回,都调用 cancel() 来释放上下文资源
defer cancel()
// 使用 go-redis 的 Get 方法
data, err := s.client.Get(ctx, token).Bytes()
if err != nil {
// 如果 key 不存在go-redis 会返回 redis.Nil 错误
if errors.Is(err, redis.Nil) {
return nil, false, nil
}
return nil, false, err
}
return data, true, nil
}
// Commit 方法将 session 数据和过期时间存入 Redis。
// 如果 token 已存在,则更新其数据和过期时间。
func (s *RedisStore) Commit(token string, b []byte, expiry time.Time) error {
// ✅ 最佳实践: 为数据库操作创建带超时的上下文
ctx, cancel := context.WithTimeout(context.Background(), redisTimeout)
// ✅ 必须: 无论函数如何返回,都调用 cancel() 来释放上下文资源
defer cancel()
// 计算 Redis 的 TTL (Time To Live)
// time.Until(expiry) 会计算出当前时间到 expiry 之间的时间差
ttl := time.Until(expiry)
// 使用 go-redis 的 Set 方法,并设置过期时间
// 如果 expiry 时间已经过去ttl 会是负数Redis 会立即删除这个 key这正是我们期望的行为。
err := s.client.Set(ctx, token, b, ttl).Err()
if err != nil {
return err
}
return nil
}
// Delete 方法根据 session token 从 Redis 中删除 session 数据。
func (s *RedisStore) Delete(token string) error {
// ✅ 最佳实践: 为数据库操作创建带超时的上下文
ctx, cancel := context.WithTimeout(context.Background(), redisTimeout)
// ✅ 必须: 无论函数如何返回,都调用 cancel() 来释放上下文资源
defer cancel()
// 使用 go-redis 的 Del 方法
err := s.client.Del(ctx, token).Err()
if err != nil {
return err
}
return nil
}