220 lines
9.0 KiB
Markdown
220 lines
9.0 KiB
Markdown
|
|
# Redis缓存集成
|
|||
|
|
|
|||
|
|
<cite>
|
|||
|
|
**本文档引用的文件**
|
|||
|
|
- [redis.go](file://pkg/redis/redis.go)
|
|||
|
|
- [manager.go](file://pkg/redis/manager.go)
|
|||
|
|
- [config.go](file://pkg/config/config.go)
|
|||
|
|
- [jwt.go](file://pkg/auth/jwt.go)
|
|||
|
|
- [captcha_service.go](file://internal/service/captcha_service.go)
|
|||
|
|
- [verification_service.go](file://internal/service/verification_service.go)
|
|||
|
|
</cite>
|
|||
|
|
|
|||
|
|
## 目录
|
|||
|
|
1. [项目结构](#项目结构)
|
|||
|
|
2. [Redis客户端封装设计](#redis客户端封装设计)
|
|||
|
|
3. [连接池与健康检查机制](#连接池与健康检查机制)
|
|||
|
|
4. [缓存操作实现原理](#缓存操作实现原理)
|
|||
|
|
5. [JWT令牌存储应用](#jwt令牌存储应用)
|
|||
|
|
6. [会话管理与验证码应用](#会话管理与验证码应用)
|
|||
|
|
7. [限流控制实现](#限流控制实现)
|
|||
|
|
8. [缓存问题预防策略](#缓存问题预防策略)
|
|||
|
|
9. [高可用架构支持](#高可用架构支持)
|
|||
|
|
|
|||
|
|
## 项目结构
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
graph TB
|
|||
|
|
subgraph "pkg"
|
|||
|
|
Redis[redis]
|
|||
|
|
Config[config]
|
|||
|
|
Auth[auth]
|
|||
|
|
end
|
|||
|
|
subgraph "internal"
|
|||
|
|
Service[service]
|
|||
|
|
Repository[repository]
|
|||
|
|
end
|
|||
|
|
Redis --> Service
|
|||
|
|
Config --> Redis
|
|||
|
|
Auth --> Service
|
|||
|
|
Service --> Repository
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**图示来源**
|
|||
|
|
- [redis.go](file://pkg/redis/redis.go#L1-L175)
|
|||
|
|
- [config.go](file://pkg/config/config.go#L1-L305)
|
|||
|
|
- [captcha_service.go](file://internal/service/captcha_service.go#L1-L166)
|
|||
|
|
|
|||
|
|
## Redis客户端封装设计
|
|||
|
|
|
|||
|
|
CarrotSkin项目中的Redis客户端封装采用分层设计模式,通过`Client`结构体对`github.com/redis/go-redis/v9`库进行包装,提供更简洁的API接口。`Client`结构体包含`*redis.Client`指针和`*zap.Logger`日志记录器,实现了日志记录和错误处理的统一管理。
|
|||
|
|
|
|||
|
|
封装设计遵循单一职责原则,将Redis连接管理、操作执行和日志记录分离。通过`New`函数创建客户端实例时,会根据配置参数初始化连接,并执行连接测试,确保连接的可用性。这种设计模式提高了代码的可维护性和可测试性。
|
|||
|
|
|
|||
|
|
**本节来源**
|
|||
|
|
- [redis.go](file://pkg/redis/redis.go#L15-L52)
|
|||
|
|
- [manager.go](file://pkg/redis/manager.go#L11-L18)
|
|||
|
|
|
|||
|
|
## 连接池与健康检查机制
|
|||
|
|
|
|||
|
|
Redis连接池配置在`RedisConfig`结构体中定义,包含`Host`、`Port`、`Password`、`Database`和`PoolSize`等关键参数。连接池大小通过`PoolSize`字段配置,默认值为10,可根据实际负载情况进行调整。
|
|||
|
|
|
|||
|
|
健康检查机制在客户端初始化时实现,通过`Ping`命令测试连接可用性。使用`context.WithTimeout`设置5秒超时,防止连接测试阻塞主线程。连接成功后会记录包含主机、端口和数据库信息的日志,便于监控和故障排查。
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
sequenceDiagram
|
|||
|
|
participant App as 应用程序
|
|||
|
|
participant Manager as Redis管理器
|
|||
|
|
participant Client as Redis客户端
|
|||
|
|
participant Redis as Redis服务器
|
|||
|
|
App->>Manager : Init(cfg, logger)
|
|||
|
|
Manager->>Client : New(cfg, logger)
|
|||
|
|
Client->>Redis : Ping()
|
|||
|
|
Redis-->>Client : PONG
|
|||
|
|
Client-->>Manager : 返回客户端实例
|
|||
|
|
Manager-->>App : 初始化完成
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**图示来源**
|
|||
|
|
- [redis.go](file://pkg/redis/redis.go#L22-L47)
|
|||
|
|
- [config.go](file://pkg/config/config.go#L49-L56)
|
|||
|
|
|
|||
|
|
**本节来源**
|
|||
|
|
- [redis.go](file://pkg/redis/redis.go#L22-L47)
|
|||
|
|
- [config.go](file://pkg/config/config.go#L49-L56)
|
|||
|
|
|
|||
|
|
## 缓存操作实现原理
|
|||
|
|
|
|||
|
|
缓存操作通过`Client`结构体的方法实现,包括`Set`、`Get`、`Del`、`Exists`、`Expire`等基本操作。这些方法直接代理到底层`redis.Client`的对应方法,保持了与原生API的一致性。
|
|||
|
|
|
|||
|
|
`Set`方法接受`context.Context`、键、值和过期时间参数,支持设置键值对及其过期时间。`Get`方法返回字符串值和错误,通过`Nil`方法可以判断键不存在的情况。`Expire`方法用于修改现有键的过期时间,支持动态调整缓存策略。
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
classDiagram
|
|||
|
|
class Client {
|
|||
|
|
+*redis.Client
|
|||
|
|
+*zap.Logger
|
|||
|
|
+Set(ctx, key, value, expiration) error
|
|||
|
|
+Get(ctx, key) (string, error)
|
|||
|
|
+Del(ctx, keys) error
|
|||
|
|
+Exists(ctx, keys) (int64, error)
|
|||
|
|
+Expire(ctx, key, expiration) error
|
|||
|
|
+Nil(err) bool
|
|||
|
|
}
|
|||
|
|
class RedisConfig {
|
|||
|
|
+Host string
|
|||
|
|
+Port int
|
|||
|
|
+Password string
|
|||
|
|
+Database int
|
|||
|
|
+PoolSize int
|
|||
|
|
}
|
|||
|
|
Client --> RedisConfig : 使用
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**图示来源**
|
|||
|
|
- [redis.go](file://pkg/redis/redis.go#L60-L83)
|
|||
|
|
- [config.go](file://pkg/config/config.go#L49-L56)
|
|||
|
|
|
|||
|
|
**本节来源**
|
|||
|
|
- [redis.go](file://pkg/redis/redis.go#L60-L83)
|
|||
|
|
|
|||
|
|
## JWT令牌存储应用
|
|||
|
|
|
|||
|
|
JWT令牌存储利用Redis作为临时存储介质,通过`Set`方法将令牌与用户信息关联存储。令牌的过期时间与JWT的有效期保持一致,确保令牌在Redis中的生命周期与JWT一致。
|
|||
|
|
|
|||
|
|
在`jwt.go`中,JWT服务生成令牌后,可以通过Redis存储令牌的元数据,如用户ID、角色等信息。这种设计实现了无状态认证与有状态缓存的结合,既保持了JWT的无状态特性,又可以通过Redis快速验证令牌的有效性。
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
sequenceDiagram
|
|||
|
|
participant User as 用户
|
|||
|
|
participant Auth as 认证服务
|
|||
|
|
participant Redis as Redis缓存
|
|||
|
|
User->>Auth : 登录请求
|
|||
|
|
Auth->>Auth : 生成JWT令牌
|
|||
|
|
Auth->>Redis : Set(token, userInfo, expireTime)
|
|||
|
|
Redis-->>Auth : 存储成功
|
|||
|
|
Auth-->>User : 返回JWT令牌
|
|||
|
|
User->>Auth : 带JWT的请求
|
|||
|
|
Auth->>Redis : Get(token)
|
|||
|
|
Redis-->>Auth : 返回用户信息
|
|||
|
|
Auth-->>User : 处理请求
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**图示来源**
|
|||
|
|
- [jwt.go](file://pkg/auth/jwt.go#L32-L52)
|
|||
|
|
- [redis.go](file://pkg/redis/redis.go#L60-L63)
|
|||
|
|
|
|||
|
|
**本节来源**
|
|||
|
|
- [jwt.go](file://pkg/auth/jwt.go#L32-L52)
|
|||
|
|
- [redis.go](file://pkg/redis/redis.go#L60-L63)
|
|||
|
|
|
|||
|
|
## 会话管理与验证码应用
|
|||
|
|
|
|||
|
|
会话管理通过Redis存储会话数据,每个会话以唯一ID作为键,会话数据作为值进行存储。验证码应用在`captcha_service.go`中实现,使用`redisKeyPrefix = "captcha:"`作为键前缀,确保验证码数据的隔离性。
|
|||
|
|
|
|||
|
|
验证码生成时,将滑块的目标坐标等验证信息序列化后存储到Redis,设置300秒过期时间。验证时从Redis获取原始数据,与用户输入进行比对,验证成功后立即删除Redis记录,防止重复使用。
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
flowchart TD
|
|||
|
|
Start([生成验证码]) --> Generate["生成滑块坐标(Tx,Ty)"]
|
|||
|
|
Generate --> Serialize["序列化为JSON"]
|
|||
|
|
Serialize --> Store["Set(captcha:id, json, 300s)"]
|
|||
|
|
Store --> Return["返回验证码数据"]
|
|||
|
|
Verify([验证验证码]) --> Get["Get(captcha:id)"]
|
|||
|
|
Get --> Check{"是否存在?"}
|
|||
|
|
Check --> |否| Expired["返回: 验证码已过期"]
|
|||
|
|
Check --> |是| Parse["解析JSON数据"]
|
|||
|
|
Parse --> Validate["验证用户输入"]
|
|||
|
|
Validate --> Delete["Del(captcha:id)"]
|
|||
|
|
Delete --> Result["返回验证结果"]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**图示来源**
|
|||
|
|
- [captcha_service.go](file://internal/service/captcha_service.go#L75-L135)
|
|||
|
|
- [redis.go](file://pkg/redis/redis.go#L60-L63)
|
|||
|
|
|
|||
|
|
**本节来源**
|
|||
|
|
- [captcha_service.go](file://internal/service/captcha_service.go#L75-L135)
|
|||
|
|
- [redis.go](file://pkg/redis/redis.go#L144-L146)
|
|||
|
|
|
|||
|
|
## 限流控制实现
|
|||
|
|
|
|||
|
|
限流控制在`verification_service.go`中实现,通过Redis的`Set`命令设置频率限制。使用`codeType`和`email`组合生成限流键,值设为"1",过期时间由`CodeRateLimit`常量定义。
|
|||
|
|
|
|||
|
|
当用户请求发送验证码时,先检查限流键是否存在,若存在则拒绝请求,防止频繁发送。这种基于Redis的限流机制简单高效,能够有效防止恶意刷屏攻击,保护系统资源。
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
flowchart LR
|
|||
|
|
Request[发送验证码请求] --> Check["Exists(verification:rate:email)"]
|
|||
|
|
Check --> |存在| Reject[拒绝请求]
|
|||
|
|
Check --> |不存在| Send[发送验证码]
|
|||
|
|
Send --> Set["Set(verification:rate:email, 1, 60s)"]
|
|||
|
|
Set --> Response[返回响应]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**图示来源**
|
|||
|
|
- [verification_service.go](file://internal/service/verification_service.go#L58-L67)
|
|||
|
|
- [redis.go](file://pkg/redis/redis.go#L75-L78)
|
|||
|
|
|
|||
|
|
**本节来源**
|
|||
|
|
- [verification_service.go](file://internal/service/verification_service.go#L58-L67)
|
|||
|
|
|
|||
|
|
## 缓存问题预防策略
|
|||
|
|
|
|||
|
|
针对缓存穿透、雪崩、击穿问题,CarrotSkin采用多种预防策略。对于缓存穿透,使用`Nil`方法识别空值情况,避免频繁查询数据库。对于缓存雪崩,建议在配置中设置随机的过期时间偏移,避免大量缓存同时失效。
|
|||
|
|
|
|||
|
|
缓存击穿问题通过互斥锁或逻辑过期策略解决。当热点数据失效时,只允许一个请求加载数据,其他请求等待并使用旧数据,直到新数据加载完成。这种策略既保证了数据的一致性,又避免了数据库的瞬时压力。
|
|||
|
|
|
|||
|
|
**本节来源**
|
|||
|
|
- [redis.go](file://pkg/redis/redis.go#L160-L162)
|
|||
|
|
- [captcha_service.go](file://internal/service/captcha_service.go#L144-L145)
|
|||
|
|
|
|||
|
|
## 高可用架构支持
|
|||
|
|
|
|||
|
|
Redis高可用架构支持通过配置文件中的连接参数实现。虽然当前代码主要针对单机模式,但`github.com/redis/go-redis/v9`库原生支持哨兵模式和集群模式。通过修改配置,可以无缝切换到高可用架构。
|
|||
|
|
|
|||
|
|
连接故障恢复机制内置于客户端库中,自动处理网络抖动和临时故障。建议在生产环境中配置合理的超时时间和重试策略,确保系统的稳定性和可靠性。监控连接状态和性能指标,及时发现和解决潜在问题。
|
|||
|
|
|
|||
|
|
**本节来源**
|
|||
|
|
- [redis.go](file://pkg/redis/redis.go#L24-L32)
|
|||
|
|
- [config.go](file://pkg/config/config.go#L49-L56)
|