单状态模型架构
Source: Dev.to
问题陈述
现代系统架构常常在追求规模和灵活性的同时牺牲了简洁性和一致性。在急于采用微服务、事件驱动模式以及复杂的云工具时,许多系统最终出现了 碎片化状态,分散在各个服务中。每个服务可能都有自己的数据库或缓存,导致同一信息出现 多个真相来源。
这种碎片化会导致不一致——例如,用户界面显示的值与后端服务或日志中的值不一致,仅仅因为系统的不同部分不同步。这种分歧使得排查问题像在解谜,因为没有单一组件了解系统状态的 “完整故事”。
此外,过度抽象和炒作驱动的设计 会使情况更糟。层层中间件、无数 API 以及过度工程化的框架往往在没有明确收益的情况下增加了复杂度。团队可能会采用 “无状态” 的基于令牌的认证或过度异步的通信,声称是遵循现代最佳实践——却发现没有人知道某条数据真正存放在哪里,或系统的哪个部分拥有权威性。
结果: 一个技术上很先进但难以推理、行为不可预测的系统。
简而言之,许多现代架构忽视了 清晰性和单点状态责任。缺乏统一的状态管理方法,系统变得更难维护,用户会遭遇混乱或不一致的体验。我们需要回归一种架构,让简洁、可预测以及关键状态数据的单一真相成为核心。
单一状态模型的定义
单一状态模型 是一种架构方法,它为每个用户会话在整个系统中建立唯一的权威状态。
在该模型中,表示用户当前会话和上下文的所有数据都保存在 一个地方(单一状态存储),而不是在每个服务中复制或派生。每个应用和组件都从 同一真相之源 获取会话特定信息。
关键要点
- 会话状态的 单一真相来源 将原本分散在多个服务中的数据统一。
- 与其让每个微服务或前端保有自己的用户上下文副本(导致不同步),模型坚持 唯一的中心会话状态 为事实依据。
- 所有其他层(缓存、UI 显示、日志)仅是该唯一状态的映射。
- 通过为每个用户会话拥有一个规范的状态对象,系统能够在用户与应用的任何交互中提供 一致且最新的视图。
设计原则与非目标
设计原则
- 单一真相来源: 每一条会话特定数据都存储并更新在同一个位置(中心会话存储),消除冲突的版本。
- 状态统一化: 将分散的状态统一为单一逻辑模型。用户身份、偏好、角色等上下文共享同一状态空间。
- 简洁胜于复杂: 设计倾向于直接、可预测的行为,而不是巧妙的抽象。
- 强一致性: 会话状态的变更对系统所有部分立即可见。模型更倾向于同步或原子更新,而非最终一致性。
- 透明性: 工程师和运维人员可以指向唯一的会话记录,了解用户的当前状态。
非目标
- 无限水平扩展: 该模型有意将会话信息重新集中,并不追求为任意规模而完全分区的状态设计。
- 多语言或每服务专属数据模型: 它不满足每个服务拥有自己定制的用户状态视图,而是统一会话数据模型。
- 为炒作而追新技术: 区块链或奇特共识算法等技术被回避;目标是可预测性,而非跟风。
- 取代长期记录数据库: 单一会话状态 不是 所有数据的长期系统记录(例如订单历史)。它只涉及 活动会话状态——在用户交互期间的短暂、可变数据。
最小逻辑架构
核心组件
- 身份提供者 (IdP): 负责验证凭证并签发身份令牌的认证服务。它是入口守卫,但不处理会话状态。
- 网关 (Gateway): 统一入口点,验证用户身份并从存储读取/写入用户会话状态。它既是流量调度员也是会话促进者。
- 会话存储 (Session Store): 模型的核心——一个集中仓库(如 Redis、SQL),以单一结构化对象保存每个用户的会话状态。
- 应用 (Services): 处理业务逻辑的后端服务。它们 对用户会话保持无状态,依赖网关提供上下文。
- 客户端 (Client): 终端用户界面(浏览器/APP),仅保留最小状态(通常只有一个令牌)。
组件架构图
flowchart LR
subgraph Client_Side [Client Side]
user[User
(Browser/App)]
end
subgraph Server_Side [Server Side]
gateway[Gateway]
idp[Identity
Provider]
session[Session
Store]
subgraph Apps [Applications]
app1[Application 1]
app2[Application 2]
end
end
user -- Login / Requests --> gateway
gateway -- Verify Identity --> idp
idp -- User Identity --> gateway
gateway -- Read/Write Session --> session
gateway -- Forward request --> app1
gateway -- Forward request --> app2
app1 -- (If needed) Read/Update --> session
app2 -- (If needed) Read/Update --> session
会话生命周期示例
生命周期时序图
sequenceDiagram
participant User
participant IdP as Identity Provider
participant Gateway
participant Session as Session Store
participant AppA as Application A
participant AppB as Application B
User ->> IdP: Submit login credentials
IdP -->> User: Return authentication token (if valid)
User ->> Gateway: Open App A (with token)
Gateway ->> IdP: Validate token/identity
IdP -->> Gateway: Identity confirmed
Gateway ->> Session: Create new session for user (store state)
Session -->> Gateway: Session created (ID and data)
Gateway ->> AppA: Forward request with user context
AppA ->> Session: Update session data (e.g., user updates profile)
Session -->> AppA: Confirm update
AppA -->> Gateway: Send response for App A request
Gateway -->> User: Response from App A (reflecting updated state)
%% User accesses another application in the same session
User ->> Gateway: Open App B (with token or session ID)
Gateway ->> Session: Fetch current session state
Session -->> Gateway: Return session data (including latest updates)
Gateway ->> AppB: Forward request with user context
AppB -->> Gateway: Send response for App B request
Gateway -->> User: Response from App B (consistent with updated state)