时序图展示一组对象、组件或系统如何随时间相互交互。它捕捉参与者之间传递的消息序列——谁向谁发送了什么,以及按什么顺序。
时序图属于 UML(统一建模语言)家族,但你不需要了解 UML 才能使用它。核心概念很直观:参与者显示为列,时间向下流动,消息是连接参与者的水平箭头。读懂时序图只需要从上到下跟着箭头走。
时序图最常用于软件工程中,记录系统在特定场景下的行为方式:用户登录流程、支付处理序列、API 请求响应周期、微服务交互。它的价值在于让隐式的行为变得显式——迫使你指明每一条消息、每一个响应以及每一个涉及的系统。
如何读懂时序图
时序图有五个需要识别的元素:
参与者(也称为角色或对象) 在顶部以带标签的方框或小人图标出现。参与者可以是用户、系统、服务、数据库,或任何发送或接收消息的实体。
生命线 是从每个参与者向下延伸的垂直虚线。它们代表参与者在整个交互过程中的存在。
消息 是生命线之间的水平箭头,代表通信——函数调用、HTTP 请求、数据库查询、用户操作。每条消息应该标注它是什么。
- 实线箭头(
—>)= 同步消息(发送方等待响应) - 空心箭头(
-->)= 异步消息(发送方不等待) - 虚线箭头(
- - >)= 响应 / 返回值
激活条 是画在生命线上的细长矩形,表示该参与者处于活跃状态——正在处理请求或等待响应返回。
时间向下流动。 图的顶部是更早的时刻,底部是更晚的时刻。图中更靠上的消息发生在更靠下的消息之前。
示例:用户登录流程
这个示例展示用户提交登录表单时发生的事情:
用户 浏览器 API 服务器 数据库
| | | |
| 输入凭据 | | |
| 提交表单 | | |
| | POST /login | |
| | 凭据 | |
| | | SELECT 用户 |
| | | WHERE email |
| | | |
| | | 返回用户记录 |
| | | |
| | | 验证密码哈希 |
| | | (内部操作) |
| | | |
| | 200 OK | |
| | 会话令牌 | |
| | | |
| 跳转到 | | |
| 仪表盘 | | |
从上到下读:用户输入凭据并提交表单,浏览器发送 POST 请求到 API,API 查询数据库找到用户记录,收到后验证密码哈希(注意:这里没有箭头出去——这是本地计算),然后返回带会话令牌的 200 OK 响应,浏览器随后跳转到仪表盘。
图表揭示了一些用文字描述不容易表达的东西:
- 浏览器不直接与数据库通信——所有请求都通过 API
- 密码验证在 API 中进行,不在数据库中
- 数据库返回完整的用户记录,而不只是「密码匹配」的布尔值
更多示例
支付处理
客户 前端 API 服务器 支付网关 数据库
| | | | |
| 提交 | | | |
| 支付 | | | |
| | POST /pay | | |
| | | 扣款 | |
| | | | 处理交易 |
| | | 批准码 | |
| | | | |
| | | INSERT 订单 | |
| | | | 订单已保存|
| | 确认页面 | | |
| 收据邮件 | | | |
图表揭示了顺序依赖:扣款必须成功才能保存订单。如果扣款失败,不会发生数据库写入。
微服务:订单通知
订单服务 事件总线 通知服务 邮件提供商
| | | |
| 订单已确认 | | |
| 发布 | | |
| order.placed | | |
| | 订阅: | |
| | order.placed |
| | | 获取用户邮箱 |
| | |(从用户服务) |
| | | |
| | | 通过提供商 |
| | | 发送邮件 |
| | | |
| | | 发送回执 |
图表让异步的本质变得清晰:订单服务发布后就不管了,它不知道邮件是否发出、何时发出。通知服务独立处理这些。
带错误处理的 API 调用
客户端 API 缓存 数据库
| | | |
| GET /item/42| | |
| | 缓存查找 | |
| | | 缓存未命中 |
| | | |
| | 数据库查询 | |
| | | 数据库:找到
| | 存入缓存 | |
| | | 已存储 |
| | | |
| 200 + item | | |
| | | |
| GET /item/99| | |
| | 缓存查找 | |
| | | 缓存未命中 |
| | 数据库查询 | |
| | | 数据库:未找到
| 404 未找到 | | |
这张图同时记录了正常路径(找到商品)和错误路径(未找到商品),以及缓存层在两种情况下的行为。
时序图符号速查
| 符号 | 含义 |
|---|---|
实线箭头 —> | 同步消息或调用 |
虚线箭头 --> | 返回值 / 响应 |
空心箭头 - -> | 异步消息 |
| 生命线上的激活条(矩形) | 参与者正在处理 |
方括号中的 [条件] | 守卫条件——只有条件为真时才执行 |
loop [n 次] 框 | 重复序列 |
alt [条件] 框 | 可选路径(类似 if/else) |
opt [条件] 框 | 可选序列 |
par 框 | 并行序列 |
ref 框 | 引用另一张图表 |
alt、opt 和 loop 框被称为组合片段——它们允许你展示条件和重复行为,而无需为每条分支画单独的图表。
时序图 vs. 其他 UML 图
| 图表类型 | 展示内容 | 最适合 |
|---|---|---|
| 时序图 | 参与者之间的消息顺序 | 记录特定流程 |
| 类图 | 静态结构、关系 | 数据模型设计 |
| 状态机图 | 状态转换 | 有复杂状态的对象 |
| 活动图 | 流程控制 | 业务流程、算法 |
| 组件图 | 系统架构 | 高层架构 |
| 用例图 | 用户目标 | 需求收集 |
时序图是回答「X 做 Y 时会发生什么,按什么顺序」这类问题的正确选择。对于静态结构、状态管理或高层架构的问题,它们不是合适的工具。
什么时候画时序图
实现新功能之前。 在写代码之前画出时序图,迫使你思考每一条消息,发现边缘情况、缺失的错误路径或不清晰的服务职责。
设计 API 时。 时序图让请求响应契约在任何代码编写之前就变得具体且可审查。
调试分布式系统故障时。 当多服务交互在生产中失败,绘制预期消息序列并与追踪记录对比,往往能揭示故障发生的位置。
开发者入职培训时。 系统中最重要流程的时序图,给新工程师提供了通过读代码需要数周才能拼凑出来的上下文。
对齐服务职责时。 当某项功能应该由哪个服务负责存在歧义时,时序图明确地把问题表达出来:这个箭头应该属于哪里?
时序图不适合的场景
高层架构概览。 如果你想展示哪些服务存在、它们总体上如何连接,组件图更清晰。拥有 20 个服务的系统的时序图会难以阅读。
静态关系。 如果你想展示数据库表之间的关系,用 ER 图,不是时序图。
所有可能的场景。 时序图应该记录一个特定的、定义良好的场景。不要试图在一张图中展示所有可能的路径——对重要分支使用 alt 块,但保持专注。
常见问题
时序图应该有多详细? 包含所有对被记录行为有实质影响的参与者。省略基础设施关注点(负载均衡器、日志记录),除非它们是关注重点。如果图表有超过 6-7 个参与者,考虑拆分成子图表。
应该包含错误路径吗? 对于所有与生产相关的错误,是的。时序图中缺少错误路径是 Bug 的常见来源——图表记录了正常路径,但没有指定数据库宕机或外部 API 返回错误时会发生什么。
时序图和流程图有什么区别? 流程图展示单一流程中有决策分支的步骤。时序图展示多个不同参与者之间的消息。流程图回答「接下来发生什么」——时序图回答「谁做什么,按什么顺序」。
时序图必须严格遵守 UML 符号吗? 在实际使用中,不必。很多团队使用简化版本——参与者用方框、消息用线条、加上标注。UML 符号为复杂场景(组合片段、激活条)增加了精确性,但对于基本文档来说不是必须的。
