外观
一句话答案
推荐 Redis 集中存储 Session(简单高效),也可用 JWT Token(无状态但无法主动失效)。
核心要点
问题背景: 负载均衡下,同一客户端的多次请求可能被分发到不同服务器,服务器之间需要共享 Session 才能正确处理请求。
四种方案对比:
| 方案 | 原理 | 优点 | 缺点 |
|---|---|---|---|
| 粘性 Session(Sticky Session) | 负载均衡器对源 IP 做哈希,同一 IP 始终路由到同一服务器 | 实现简单,无需改代码 | 服务器宕机则该 IP 用户 Session 全部丢失;负载不均衡 |
| Session 复制/同步 | 集群内通过广播同步 Session 数据,每台服务器都有全量 Session | Session 不丢失,任意节点可处理请求 | 同步开销大,集群规模受限;内存占用翻倍增长 |
| 集中式存储(推荐) | 用 Redis / 数据库集中存储 Session,所有服务器读写同一存储 | 扩展性好,服务器无状态;Session 不随服务器宕机丢失 | 依赖外部存储的可用性;增加一次网络 IO |
| 客户端存储 | 将 Session 数据存在 Cookie 中(或使用 JWT),客户端每次请求自带 | 服务端完全无状态,扩展性最好 | Cookie 大小限制(4KB);安全性低(数据在客户端);无法服务端主动失效 |
方案一:粘性 Session(Sticky Session)
实现:Nginx 的 ip_hash 指令
upstream backend {
ip_hash; # 同一 IP 固定路由到同一后端
server 10.0.0.1:8080;
server 10.0.0.2:8080;
server 10.0.0.3:8080;
}
问题:
① 服务器宕机 → 该服务器上的所有 Session 丢失
② 某些 IP 段请求量大 → 负载不均
③ 客户端使用代理时,大量用户共享同一 IP → 热点问题方案二:Session 复制
实现:Tomcat 集群的 DeltaManager 广播同步
问题:
每新增一个 Session → 广播到 N-1 台机器 → O(N^2) 通信量
适合小集群(3~5 台),大规模集群不可用方案三:Redis 集中存储(推荐)
实现:Spring Session + Redis
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 1800)
public class SessionConfig {
// Spring 自动将 HttpSession 存储到 Redis
}
Redis 中存储结构:
Key: spring:session:sessions:<sessionId>
Value: Hash(属性名 → 属性值)
TTL: 30 分钟(可配置)
优势:
✅ 服务器完全无状态,随时水平扩展
✅ Redis 高性能(内存存储 + 主从高可用)
✅ Session 不随某台服务器宕机丢失方案四:JWT 无状态方案
流程:
1. 用户登录 → 服务端生成 JWT Token(包含用户 ID、角色、过期时间等)
2. 返回 Token 给客户端(存 Cookie / LocalStorage)
3. 客户端每次请求携带 Token(Header: Authorization: Bearer xxx)
4. 服务端验证 Token 签名 → 提取用户信息 → 无需查 Session
优势:完全无状态,服务端不存储任何东西
劣势:Token 无法主动失效(除非引入黑名单机制,又回到有状态)推荐方案:Redis 集中存储 + JWT 结合
JWT 存放轻量信息(用户 ID、角色),用于鉴权
需要存储的会话数据(购物车、用户偏好等)放 Redis
兼顾无状态的可扩展性和服务端数据管理的灵活性追问与易错
追问方向:
- JWT 怎么让 Token 失效?
- Redis Session 挂了怎么办?
- JWT Token 太大影响性能吗?
易错点:
- ❌ JWT 就是安全的——不做加密不应放敏感信息
- ❌ Redis Session 和 JWT 选一个——可以结合
💡 记忆锚点
四种方案像存行李:粘性Session=每次去同一个柜子(柜子坏了行李没了);Session复制=每个柜子都放一份(浪费空间);Redis集中存储=寄存处统一保管(推荐,服务器无状态随便扩);JWT=行李自己背(无状态但不能远程锁箱子)。最佳实践:JWT做身份证+Redis存行李。