Files
easygoadmin-wiki/EasyGoAdmin_GoFrame_AntdVue/zh/architecture/cache.md
T
2026-10-20 21:53:26 +08:00

95 lines
4.8 KiB
Markdown
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.
# 4.8 缓存架构设计
:::tip 概述
缓存实现为 utility/cache 统一门面:Redis 优先,连接失败或运行期出错自动降级进程内存 gcache(UsingMemory() 可查)。三层缓存各司其职,Redis 键统一前缀 easygoadmin:。
:::
## 缓存分层图
```text
请求 ──▶ ① 进程内缓存(sync.Map,无 Redis 交互,重启即清)
dictTextCache(300s) · reqFieldCache/textPlanCache(反射计划,永不过期)
maskReCache(正则,永不过期)
──▶ ② Redis(跨实例共享,TTL 自动回收)
黑名单/在线/验证码/字典/租户摘要/身份/限流/任务锁
──▶ ③ 数据库(唯一真相,dao.Ctx 全量过滤条件)
设计原则:② ③ 之间的一切缓存都必须有明确的失效点或短 TTL 兜底;
① 仅缓存"计算计划/只读文本",不允许缓存业务状态。
```
## Redis 键全集
| 键模式 | TTL | 写入方 | 读取方 |
|--------|-----|--------|--------|
| `easygoadmin:token:blacklist:{jti}` | 剩余有效期+60s | Logout/Refresh 轮换/RemoveToken/ForceLogout | JWT 中间件 Exists 判定 |
| `easygoadmin:online:jti:set`(zset) | 成员随 info(score=过期 ts) | MarkOnline/MarkOffline | OnlineUser.Page(ZRangeByScore≥now) |
| `easygoadmin:online:info:{jti}` | access 剩余+60s | MarkOnline/MarkOffline | OnlineUser.Page/Detail/ForceLogout |
| `easygoadmin:captcha:{uuid}` | 300s | Captcha 生成 | Login/Verify(一次性) |
| `easygoadmin:dict:item:{code}` | 3600s | dict.setCache/RefreshCache | dict_item 快捷接口/AliasList |
| `easygoadmin:tenant:info:{id}` | 60s | Tenant 中间件/Login 路径 | Tenant 中间件/Login |
| `easygoadmin:user:identity:{uid}` | 300s | JWT 中间件(回源 Loader) | JWT 中间件(Roles/Perms/DataScope/DeptId JSON) |
| `easygoadmin:user:perms:{uid}` / `user:roles:{uid}` | 约定键(保留扩展) | — | 主路径用 identity 合并键,变更时一并清理 |
| `ratelimit:{ip}:{path}:{窗口起点}` | window+5s | RateLimit incr | RateLimit |
| `job:lock:{jobId}` | 300s 兜底 | jobTryLock(NX) | jobTryLock(执行完 defer Del) |
## 主动失效点清单
**身份缓存 `user:identity:{uid}` 删除时机**:
```text
* role.SavePermission 保存角色权限 → clearRoleIdentityCache(roleId)
→ 查该角色全部 user → 逐个 Del
* role.Update 修改角色(含 data_scope 变更)→ AfterUpdate → 同上
* user.AssignRoles 分配/调整角色 → clearIdentityCache(userId)
(user.Update / user.Add 均经 AssignRoles——改部门 dept_id 亦在此清缓存)
* 版本兼容:Identity 新增字段反序列化缺省为 0,读侧 GetDataScope 回退"全部"
——升级无需强清存量缓存
```
**其他**:租户摘要在 tenant 状态/过期变更时由 AfterUpdate clearCache 钩子清理;字典 Redis 缓存由 RefreshCache 主动重建(dict_item 变更不主动清,最长 1 小时延迟);黑名单靠 TTL 自然过期(过期即令牌本身已失效);验证码命中即删。
## 进程内缓存明细
| 缓存 | 位置 | 内容 | TTL |
|------|------|------|-----|
| dictTextCache | base.go | {code}:{value} → 显示名 | 300s |
| reqFieldCache / textPlanCache / childrenIdxCache | module_spec.go | 反射索引与填充计划 | 永不过期(类型不变即安全) |
| maskReCache | middleware/util.go | 脱敏正则 | 永不过期 |
## 内存降级的影响面
Redis 不可用时 `UsingMemory()=true`,以下能力**退化单机语义**:
```text
限流计数 各实例各自计数(阈值实际翻倍)
任务锁 job:lock 退化单机,多实例可能并发执行任务
在线用户 只看本实例登录的会话
JWT 黑名单 本实例签发的登出/强退不拦截其他实例
验证码 单实例内有效
字典缓存 各实例独立回源
```
运维可通过 `GET /api/cache/info` 的 `mode` 字段确认当前模式(redis/memory)。
## TTL 设计原则(新增缓存遵循)
```text
1) 状态判定类(黑名单/租户状态):TTL ≤ 业务有效期,且必须有主动失效点;
宁可短 TTL 多回源,不可长 TTL 陈旧放行
2) 展示文本类(字典/归属地):TTL 300s~3600s + 刷新接口,接受分钟级延迟
3) 高频读聚合类(identity):合并为单键 JSON(一次回源装多个集合),
TTL 300s,变更路径主动 Del
4) 计数/锁类:键内嵌窗口起点或场景唯一 id,TTL 只做回收不参与语义
5) 任何经 cache.Set 的值必须可 JSON 序列化;读侧 Unmarshal 失败视为未命中回源
```
## 排障速查
```text
键不存在/值陈旧 先查是否内存降级(redis-cli KEYS 'easygoadmin:*' 对比)
权限不生效 DEL easygoadmin:user:identity:{uid}
租户被禁仍可访问 等 60s 或 DEL easygoadmin:tenant:info:{id}
多实例行为不一致 确认 Redis 可用(/api/cache/info mode 应为 redis)
```