第17章 会话状态模式
在客户端、服务器内存和数据库之间选择会话状态的权威位置。
学习目标
- 能为订单向导会话选择客户端、服务器内存或数据库作为状态权威,并说明安全与恢复理由
- 能用会话键、版本、容量、过期策略和多节点路由记录一次可复核的状态设计
- 能在正常续期、并发提交、节点故障和过期场景中验证状态一致性,并识别何时撤回
为什么第17章会话状态模式值得单独学习
↡把会话状态放在客户端,由客户端请求持续携带;服务端通过签名、版本和大小限制验证其可信度。客户端会话状态减少服务器存储和节点同步,但不能把未经保护的客户端字段当成事实来源。订单折扣、地址和权限等状态必须区分可重建数据与不可由客户端决定的授权事实。
↡把会话状态保存在处理请求的服务器进程或节点内存中,访问快但依赖路由和节点生命周期。服务器会话状态适合短生命周期、低延迟和可接受丢失的向导,但多节点部署需要粘性路由、复制或明确的故障降级策略。
↡把会话状态持久化在共享数据库或等价存储中,使请求可跨节点恢复并依据版本控制更新。数据库会话状态提供跨节点恢复和可查询的生命周期,但引入读写延迟、清理任务、容量规划和并发冲突处理。
先建立直觉:状态位置就是责任声明
↡标识一次会话的不可猜测令牌,通常只在客户端携带,服务端用它定位或验证状态。会话键只负责定位或关联状态,不应直接承载未经签名的权限结论;轮换、撤销和重放防护都要写进设计记录。
↡规定会话在多久未活动、达到固定时间或完成某个业务事件后失效并被清理的规则。过期不是一个孤立的时间常量,而是安全、用户体验、清理成本和恢复目标之间的可验证取舍。
先预测:订单向导在三个节点上运行,用户可以回到上一步修改地址,提交请求可能重试两次。把状态放在客户端、单节点内存还是共享数据库,最先要比较的不是框架偏好,而是会话键可信度、多节点恢复、版本冲突和过期后的补救。
会话状态模式的关键问题是“谁拥有当前事实、谁能修改它、失败后能恢复到什么状态”。状态位置决定数据是否需要签名、请求是否需要路由到同一节点、节点故障是否丢失进度,以及清理任务是否能按租约稳定运行。
目录单元到教学证据
第17章 会话状态模式
在 第17章 会话状态模式 的单元边界内,学习者要用订单向导会话比较三个目录节点:Client Session State、Server Session State 和 Database Session State。单元键为 poeaa24-chapter-17-session-state-patterns;核心证据是同一应用切片在正常续期、节点切换、并发提交和过期后都能说明状态权威与恢复路径。
评审记录还要回答:会话键如何生成和轮换,客户端字段如何签名或加密,服务器节点如何扩缩容,数据库如何清理过期记录,版本冲突由谁拒绝和重试。如果答案只写“由框架 session middleware 处理”,就无法在架构改变后复核。
专属代码案例:订单向导会话
下面的代码使用共享存储的版本号保护并发更新。它不实现具体数据库,而是让状态权威、版本检查、过期规则和失败信号可观察。
type CheckoutState = {
address: string;
updatedAt: number;
expiresAt: number;
};
type SessionRecord = { version: number; state: CheckoutState };
class VersionConflict extends Error {}
function updateSession(
record: SessionRecord,
expectedVersion: number,
patch: Partial<CheckoutState>,
now: number,
): SessionRecord {
if (record.version !== expectedVersion) {
throw new VersionConflict("session changed");
}
if (now >= record.state.expiresAt) {
throw new Error("session expired");
}
return {
version: record.version + 1,
state: { ...record.state, ...patch, updatedAt: now },
};
}这段代码把四项证据固定下来:状态由共享记录权威持有,客户端必须带预期版本,过期状态不能继续写入,并发更新返回明确冲突而不是静默覆盖。生产实现还需补充会话键轮换、签名或加密、原子写入、清理指标和重试幂等性。
三种状态位置
状态位置图把客户端、服务器节点和共享数据库放在同一条请求路径上,帮助评审比较带宽、路由、恢复和一致性责任,而不是只看访问速度。
故障与过期边界
会话设计必须把正常续期、并发版本冲突、节点故障和过期清理分开验证。无论选择哪个位置,都要给出会话键失效、状态恢复、用户提示和重试的可重放证据。
选择与拒绝矩阵
| 评审问题 | 选择会话状态位置的证据 | 应拒绝当前方案的信号 |
|---|---|---|
| 权威 | 清楚说明事实由客户端、节点内存或共享存储拥有 | 客户端字段直接决定权限或价格 |
| 扩缩容 | 多节点路由、复制或共享恢复策略可测量 | 请求依赖粘性节点却没有故障降级 |
| 并发 | 版本、原子写入和重试行为明确 | 重试会静默覆盖其他步骤的更新 |
| 过期 | Expiration Policy 与清理、提示和恢复路径一致 | 过期只删除记录,没有用户可理解的补救 |
| 替代 | 已比较三个位置并保留撤回路径 | 只因框架默认 session 存储而选择 |
常见误区
可验证练习
练习
本组练习围绕 第17章 会话状态模式,要求把状态权威、失败边界和恢复目标写入同一张评审卡。
问题 1:选择位置。 订单向导需要跨三台节点运行,状态包含地址和步骤,不包含不可重建的支付凭据。如何比较三个状态位置?
问题 2:处理并发。 用户在两个浏览器标签页同时修改地址,第二次提交带着旧版本。应该覆盖,还是拒绝?
问题 3:处理过期与故障。 服务器内存会话在节点重启后丢失,且用户已离开两小时。系统应该怎样响应?
名词解释
名词解释
本章出现的专业名词,用大白话再讲一遍。
- Client Session State
由客户端携带会话数据、服务端通过签名版本和大小限制进行验证的状态位置。
- Server Session State
保存在服务器进程或节点内存中的会话状态,访问快但依赖路由和节点生命周期。
- Database Session State
持久化在共享数据库或等价存储中的会话状态,支持跨节点恢复并需要并发与清理策略。
- Session Key
标识一次会话的不可猜测令牌,用于定位或验证状态而不是直接承载权限事实。
- Expiration Policy
规定会话在不活动、固定时间或业务事件后失效和清理的规则。
本章小结
掌握第17章会话状态模式的标志,不是记住某个 session middleware 的配置,而是能在订单向导中解释“会话键 → 状态权威 → 更新版本 → 节点故障 → 过期恢复”的责任链。客户端、服务器内存和数据库三个位置各有成本,设计必须把安全、扩缩容、并发、清理和用户补救放进同一组可重放证据;当状态权威不清、只依赖粘性路由或过期没有恢复路径时,应拒绝当前方案。
前后导航
来源与改写范围
- Martin Fowler 作者图书页:核对全书主题、教程与模式参考结构。
- Martin Fowler 模式目录:核对第17章会话状态模式及其三个公开模式节点。
- Pearson 出版社页面:交叉核对英文版出版信息与目录范围。