P12

方案设计入门练习

点击正文段落选定收藏或划线位置;未选定时使用屏幕内第一段。

课内目录

1 为什么先学「方案设计入门练习」

接到“做个订单接口”的任务,初学者常会直接画服务器、数据库和消息队列。图看起来完整,却回答不了评审中的实际问题:顾客看到成功时,订单究竟保存了吗?活动期间请求变多,哪些功能可以暂缓?预算只够有限资源时,先保护什么?方案设计的起点是把这些问题变成可讨论、可验证的约定。组件只是实现约定的手段,不能替代约定本身。

本课始终使用同一个零售场景:顾客提交商品、数量和收货信息,订单 API 创建待支付订单,随后可以查询订单状态。支付确认、库存扣减和配送属于关联流程,需要说明接口边界,但本次练习不展开它们的内部实现。我们假设购物车和商品展示已存在,订单服务需要核验商品与价格依据,并与库存服务协作。本课所有容量、价格和时限都是练习假设,不代表行业通用标准。

先给练习设定共同起点:日常峰值为每秒 100 次创建请求,活动峰值为每秒 300 次,活动持续 30 分钟;查询流量另行估算。创建成功响应的第 95 百分位耗时目标为 800 毫秒,即统计窗口内至少 95% 的成功创建请求耗时不超过该值。可用性目标暂定为每月 99.9%,月资源预算暂定为 6000 元。这些数字必须经过业务、研发和运维确认,不能因为写进模板就变成事实。

本课最终要交付的不是“最强架构”,而是能解释选择的短方案:需求有哪些证据,候选方案如何比较,采用什么决策,成本按什么口径估算,风险如何暴露,故障时如何恢复。读者只需要理解请求、服务、数据库的基本含义。事务、复制和调度的深层机制留待后续学习;这里先掌握提出问题、记录假设、组织证据和设定验收条件的方法。

从需求到验收的方案设计闭环
从需求到验收的方案设计闭环

方案设计还有沟通价值。业务人员关心顾客能否下单,开发人员关心接口行为,运维人员关心告警与恢复,财务人员关心持续费用。好的方案让这些关注点落到同一条订单链路上。比如“不能重复下单”应进一步说明是用户连续点击、客户端重发,还是服务器处理后响应丢失。情形说清楚,才知道需要怎样的请求标识、状态查询和异常提示。

2 核心概念与边界

需求描述需要发生的事情,约束描述选择受到的限制,假设描述暂时相信但尚待证实的条件。对本案例,“创建并查询待支付订单”是功能需求;“已有库存接口不可改动”是约束;“库存接口在峰值时仍能及时响应”是待验证假设。把它们混写成“接库存服务即可”,会掩盖集成风险。每项重要假设都应有负责人、验证方式和最迟确认时间。

非功能需求描述服务表现,常见内容包括响应时间、容量、可用性、安全和成本。“高性能”无法验收,“活动期间在约定负载下,创建成功请求的第 95 百分位耗时不超过 800 毫秒”才有检查入口。不过只看成功请求会漏掉大量快速失败,因此还要同时约定成功率、超时率和统计范围。不同指标一起解释体验,不能用单个漂亮数字证明系统健康。

边界决定谁负责什么。本课订单 API 负责校验请求、识别重复提交、协调库存预留、保存订单并返回可查询的结果;库存服务负责判断和预留可售数量。支付服务负责支付结果,订单服务按约定接收结果并更新状态。库存预留成功而订单未保存时,需要释放或到期回收预留。先把这个责任写清楚,再讨论具体技术,能避免双方都以为对方负责清理。

成功语义是边界中最容易漏掉的内容。这里约定:只有订单记录和重复请求映射可靠保存后,才返回创建成功;响应含订单标识和待支付状态。接口接受了请求但仍在处理时,应明确返回处理中,并提供查询办法,不能显示为已经创建成功。库存不足、参数错误和系统暂不可用也应有可区分的结果,方便客户端采取不同动作。状态码只是载体,行为约定才是核心。

架构选项是满足约定的不同组织方式;架构决策记录,简称 ADR,是解释最终选择及其理由的短文档。它通常记录背景、候选、选择、后果和重新评估条件。高可用,简称 HA,表示在部分组件失效时继续提供约定服务的能力。备份主要帮助恢复数据,增加副本主要改善部分故障下的服务连续性;两者需要配合,不能相互代替。

本课还会用到恢复时间目标 RTO 和恢复点目标 RPO。前者表达希望多久恢复服务,后者表达可接受恢复到多早的数据状态。例如一般数据灾难的演练目标可暂定 RTO 为 30 分钟、RPO 为 5 分钟;如果业务要求已确认订单绝不丢失,这个 RPO 就不满足要求,必须调整保护方式或重新协商目标。目标之间可能冲突,写出来正是为了及早发现冲突。

3 工作方法(可迁移)

先收集使用情境,再转换成可检验的需求。对每个情境,写出参与者、触发动作、期望结果和失败后的允许行为。本案例可以从“顾客在活动页提交订单,网络中断后再次提交”开始。期望结果不只是恢复页面,而是顾客能够确认原请求是否生成订单,并且不会因为重试生成重复订单。这个写法比“接口支持重试”更接近需要解决的问题。

接着建立需求表。每行至少包含编号、描述、优先级、来源、验收信号和负责人。例如 R1 表示同一用户使用同一请求标识提交相同内容,应得到同一个订单结果;验收时模拟响应丢失后重试,检查订单数量和返回标识。同一标识带来不同内容时应拒绝或明确提示冲突。请求标识的有效期也必须约定,否则“永不重复”会变成没有边界的承诺。

再列出真正可比较的候选。选项 A 在请求内完成库存预留和订单保存,完成后返回结果,便于理解和查询,但创建耗时受库存接口影响。选项 B 先可靠接收创建任务,再由后台处理,客户端先看到处理中;它能缓冲短时高峰,却增加状态查询、积压管理和结果通知的工作。候选必须覆盖同一业务目标;不能拿 A 的最终成功耗时与 B 的接收耗时直接比较。

比较时先检查硬约束,再讨论偏好。若产品要求立即得到确定结果,而目前不能接受处理中页面,B 就需要改变产品约定才能采用。剩余候选可以按正确性、用户体验、容量、成本和维护难度逐项说明。评分可以辅助讨论,但分数必须附理由,不能把猜测包装成精确计算。对最关键的不确定点安排小规模验证,例如测量库存接口在目标负载下的延迟与错误率。

本次基线选择 A,并将通知等非关键工作放到订单保存之后异步执行。这个选择建立在当前流量、团队经验以及库存接口验证结果之上。ADR 应记录:为什么暂不采用 B,A 会承受什么限制,以及什么证据触发重新评估。例如经过容量验证确认,既定预算下同步链路持续无法达到目标,且产品接受处理中状态时,再评估后台创建。触发条件比“以后优化”更能指导行动。

最后给每项重要决策配验收办法和责任人。设计评审检查约定是否完整,联调检查边界是否一致,负载测试检查容量假设,故障演练检查恢复路径。方案并非上线前写完便结束:库存接口变化、活动规模变化或成本超出假设时,都应更新相关决策。先保留清楚的因果关系,后续调整才不会只剩下“之前就是这样做的”。

4 方法续:落到订单链路

把 A 展开为可讲述的请求过程:入口确认用户身份并限制明显异常的请求;订单服务检查字段、商品与价格依据;检查请求标识;向库存服务申请预留;把订单与请求标识对应结果保存到数据库;保存成功后返回订单标识。这里只画职责与交接点,不把每个内部函数都放进图里。读图的人应该能够沿着箭头解释成功是在哪个时刻成立的。

订单创建的主链路与失败出口
订单创建的主链路与失败出口

重复请求处理需要明确并发情形。如果 2 个相同请求同时到达,仅仅“先查询有没有订单”并不充分,因为它们可能同时查到没有。需要由存储层的唯一约束等机制保证同一用户和请求标识只能对应 1 个结果;机制细节后续再学。库存预留也要关联稳定的业务标识,使重试不会重复占用库存。接口约定和存储约束要共同保护业务结果。

库存服务与订单数据库是不同的责任边界,不能因为流程图连续就假设它们能同时成功。如果库存预留完成,订单写入却失败,订单服务需要尝试释放预留,并依靠到期回收或对账处理遗漏。此时不能返回创建成功。方案要注明预留有效期、释放责任、重试上限和人工处理入口。入门阶段先把不完整状态列出来,就已经能发现很多只画正常箭头的缺陷。

另一类问题发生在数据库保存成功之后:响应在网络中丢失,顾客看到超时,但订单已经存在。客户端应带着原请求标识查询或重试,服务返回原结果。超时意味着调用方没有及时获得答案,不能直接推断操作失败。错误提示应帮助用户确认状态,避免引导用户不断生成新请求标识。这里保护的是业务事实与用户认知之间的一致性。

把耗时预算拆到链路上能帮助讨论,但不要把各环节的第 95 百分位直接相加当成端到端结果。可以先为入口、业务处理、库存访问和数据库访问分配工程预算,再用完整链路压测检验 800 毫秒目标。外部调用需要设置超时,并为后续保存和响应预留时间。重试必须有次数上限和退避安排,否则依赖变慢时,重试会继续增加负载。

HA 也要沿这条链路检查。部署多个订单实例并让流量绕过不健康实例,可以降低单个实例失效的影响;若数据库或库存接口仍只有单个故障点,整体创建能力仍会中断。跨故障域部署、数据库切换和依赖降级都要配套演练。库存不可用时可以明确暂停创建、继续查询已有订单;这种边界清晰的降级,比返回未经确认的创建成功更容易维持信任。

5 动手:模板/清单直觉

现在用短模板把思考压缩成可评审材料。先写“目标与非目标”:支持待支付订单创建和查询,覆盖重复提交与明确错误反馈;本轮不设计支付清算和配送调度。再写“负载与体验”:记录每秒 100 次和 300 次创建请求分别来自什么预测,活动持续多久,商品分布是否集中,查询流量如何估计。流量相同但请求集中抢购同一商品时,依赖压力可能完全不同。

接着填写“接口与状态”。写出身份来源、请求标识、商品数量、价格核验方式和结果查询入口;列出待支付、已支付、已关闭等业务状态,并注明哪些状态转换属于其他流程。敏感信息只在必要范围内传递,日志不直接记录完整地址、凭证或令牌。权限检查应落实到用户只能查询自己的订单,不能只依赖订单标识难以猜到。

“决策与证据”栏可以直接采用 ADR 骨架:背景为活动期间订单创建;候选为同步完成与后台创建;决定采用同步基线;理由为当前产品需要立即得到确定结果,团队能够维护现有链路;后果为创建受库存延迟影响,需要限定超时并处理预留残留;复审条件为容量或体验约束发生实质变化。证据不足的地方标记待验证,保持文档诚实比提前写满更有用。

成本栏至少区分持续费用、按量费用和人员工作。持续费用包括实例与数据库基础资源,按量费用可能包括存储、网络和日志,人员工作包括联调、值班与演练。月成本要注明计费周期、资源运行时长、容量余量和价格日期。不能把活动 30 分钟的峰值当作整月恒定负载,也不能只算业务实例而漏掉备份与观测资源。

滑块使用教学模型观察容量与费用的关系。假设单个订单实例在指定测试负载下可稳定处理每秒 120 次创建请求,实例数为 n,则粗略容量为 120×n;失去 1 个实例后为 120×(n−1)。每个实例按每月 600 元、固定费用按每月 2400 元估算。这个线性模型只描述应用层练习,数据库、库存热点和网络可能先成为瓶颈,因此滑块结果不能代替压测。

最后填写“风险与交付”:对库存响应慢、数据库写入失败、响应丢失和预算超支分别注明影响、信号、缓解动作、负责人和剩余风险。交付物包括接口约定、架构图、ADR、成本表和验收记录。检查模板是否有用,可以请同事仅凭这些材料解释失败后该查什么、由谁处理。若仍需大量口头补充,就把缺失的约定补到对应位置。

6 观测与验收信号

观测从业务问题出发。创建请求总量反映入口负载,创建成功率反映有多少请求完成约定动作,延迟分布反映等待体验,重复请求命中量帮助判断重试情况。库存不足属于业务结果,数据库不可用属于系统故障,应分开统计,同时保留面向用户的总体验指标。否则活动期间大量库存不足可能被当成故障,也可能因被排除而掩盖页面体验问题。

从用户体验连接到链路证据
从用户体验连接到链路证据

可用性指标要先写口径。练习中可将“格式和身份均有效、且库存可售的创建请求,在规定时间内得到正确结果的比例”作为服务目标的基础,同时单独统计所有用户提交的结果分布。若按 30 天时间口径计算,99.9% 对应约 43.2 分钟不可用时间;若按请求比例计算,则不能直接换算为分钟。评审时应选择明确口径,并说明依赖故障是否计入用户体验目标。

日志、指标和追踪分别提供不同证据。指标告诉我们什么时候、影响多大;日志记录特定事件与结果;追踪把同一次请求跨服务的耗时关联起来。建议保留请求标识、订单标识、结果类别、关键阶段耗时和必要错误信息,并控制访问权限及留存时间。业务标识用于关联,不应把大量订单标识当成指标标签,以免造成难以管理的维度数量。

验收至少覆盖正常、重复、过载和故障。正常场景验证订单可查询且内容正确;重复场景验证响应丢失后仍只有 1 个订单;过载场景检查限流、错误反馈与恢复;故障场景检查库存超时、数据库失败和实例退出后的行为。性能验收注明持续时间、数据规模、并发方式、预热情况和依赖配置。没有这些条件,“压测通过”难以复现,也无法解释上线差异。

HA 验收需要同时观察恢复时间和业务结果。让 1 个订单实例退出,检查剩余容量是否覆盖活动负载,客户端重试是否仍遵守原请求标识,成功订单是否可查询。数据库恢复演练则检查备份可读、恢复步骤可执行、恢复后订单状态可核对。演练结论应写出实际测量值和未覆盖范围,不要把应用实例故障演练当作整个系统灾难恢复能力的证明。

7 常见坑与反模式

常见的问题是先确定组件,再反推需求。团队决定使用消息队列之后,才把“立即创建成功”改写成“最终会创建”,却没有让产品和客户端同步理解变化。纠正方法是先写用户可见结果,再检查组件能否支撑。若选择后台创建,就必须补上处理中状态、结果查询、超时后的解释和积压处理;组件增加了,业务责任也随之增加。

另一个问题是只写正常流程。图上画了库存成功和订单保存,却没有库存预留成功后保存失败的出口。还有人把所有失败都标记成“重试”,没有区分参数错误、库存不足、响应丢失和依赖故障。重试之前应确认操作是否可以安全重复、是否沿用原标识、是否会进一步加重依赖压力。无法确定原结果时,先提供查询路径通常更有帮助。

容量讨论容易把平均值当作峰值,把实验室吞吐当作生产承诺。每秒 120 次的单实例测量只有在请求结构、数据库规模和依赖行为相近时才具有参考价值。应用实例增加后,数据库连接、库存热点与日志写入也会变化。方案中应同时记录测量条件和容量余量,并定义超过能力时怎样拒绝或排队,避免系统在未知区域持续堆积请求。

成本与 HA 也常被口号遮蔽。“多部署几个就高可用”没有回答故障域是否独立、切换是否可靠、剩余容量是否足够;“加缓存会省钱”没有说明订单状态能否容忍旧值以及维护成本。每个优化都应对应具体问题与验收证据。对本次入门练习,优先保证订单结果可信、边界清楚、故障可定位,再根据测量讨论额外组件。

文档层面的反模式是把未知写成确定事实,或把所有风险留给“运维保障”。库存容量未验证,就明确列为上线前条件;备份恢复需要演练,就安排负责人和日期;业务不接受数据丢失,就重新检查 RPO 与保护方案。方案设计的专业性体现在能管理不确定性,而不是在每个问题后都填上肯定答案。

8 案例:订单系统相关失误

设想本案例在活动预演中暴露问题:团队部署了 3 个订单实例,认为单实例每秒 120 次,所以足够支撑每秒 300 次。压测前半段正常,随后库存接口变慢,客户端遇到超时便生成新请求标识再次提交。监控只展示成功请求的平均耗时,看板仍然偏绿,客服测试人员却发现同一购物意图出现了多个待支付订单。这是教学案例,目的是练习分析责任与证据。

直接原因包括重试标识不稳定和库存调用耗时上升,但设计问题更早发生:需求只写“不重复下单”,没有定义网络超时后的行为;容量表只算实例总数,没有检查依赖上限;验收只跑稳定流量,没有注入响应丢失;看板没有展示超时与重复创建。若只给接口增加超时设置,顾客仍可能重复提交,问题链条并没有被完整处理。

整改从契约开始。客户端对同一次提交保留稳定标识,并在超时后查询原结果;服务端通过唯一约束等机制保护重复请求;库存预留沿用稳定业务标识,并补齐释放与回收责任。观测增加端到端延迟分布、超时率、重复请求结果和预留残留数量。验收重新加入响应丢失、并发重复请求和库存超时,检查最终订单数量与状态,而不只检查响应文本。

容量与 HA 也需要修正。3 个实例在失去 1 个后,教学模型只剩每秒 240 次,低于活动每秒 300 次;而增加到 4 个是否有效,仍取决于库存和数据库。团队应在预算内验证剩余容量,并决定超载时的限流与活动策略。复盘因此形成 3 类可交付改进:修订接口契约、补充验收场景、更新容量与风险记录,每项都指定负责人和完成证据。

9 排查/复盘:证据链怎么写

排查先写可观察事实,再写解释。事实可以是“14:05 至 14:08,创建请求超时比例上升,库存调用延迟同期增大”;解释可以是“库存响应变慢可能占用了订单服务的等待资源”。二者应分开记录。时间接近只能提示关联,还需要对比链路追踪、资源指标和变更记录,才能判断原因。没有证据时使用待验证假设,避免把最早想到的原因写成结论。

订单故障的证据收集与验证路径
订单故障的证据收集与验证路径

为同一次提交串起证据时,先用请求标识定位入口记录,再关联库存预留、数据库提交和响应结果。若保存成功而响应丢失,重点检查重试是否返回原订单;若库存预留成功而保存失败,重点检查释放与回收;若数据库根本没有收到请求,就继续向前检查服务与依赖等待。关联材料应脱敏,并保留必要时间信息,不能为了排障把完整用户资料复制进复盘文档。

处置顺序应由影响决定。先确认受影响用户与业务范围,再采用已经评审过的限流、暂停创建或实例恢复措施,同时保留查询已有订单的能力。操作前明确预期信号与回退条件,操作后检查错误率、延迟和订单结果是否改善。若多个动作同时实施,就很难判断哪个有效;在允许的情况下逐项变更并记录时间,能使后续复盘更可靠。

复盘记录可采用“现象—证据—判断—行动—验证”格式。现象描述用户遇到什么,证据引用指标窗口和脱敏样本,判断解释机制并标明不确定处,行动说明负责人及期限,验证要求复现原故障条件后检查结果。例如修复重复提交后,重新模拟服务保存成功但响应丢失,确认重试返回同一订单,并检查库存仅保留相应数量。验证应针对故障后果,不能只写代码已经发布。

证据链最终要回到方案文档。如果事故源于遗漏的成功语义,就更新接口约定与 ADR;如果容量假设错误,就更新成本与容量表;如果恢复步骤不可执行,就修订操作记录并重新演练。这样每次复盘都会改善下一次设计。入门练习做到这一步,便建立了完整闭环:需求可以追溯到选择,选择可以追溯到证据,故障可以推动约定发生明确变化。

放进方案里

本方案为零售订单 API 提供待支付订单创建与查询能力。订单服务负责请求校验、重复提交识别、库存预留协调与订单保存;支付确认和配送调度按外部接口约定接入。本轮以日常每秒 100 次、活动每秒 300 次创建请求为容量假设,活动持续 30 分钟;查询流量单独核算。负载来源和库存接口能力由相应负责人在上线前确认。

采用同步创建基线:库存预留完成且订单与请求结果可靠保存后返回成功,响应包含订单标识。客户端对同一次提交保持稳定请求标识,超时后查询或重试原请求。库存预留成功而保存失败时执行释放,并由到期回收和对账处理残留。通知等工作在订单保存后执行,其失败不得改变已确认的创建结果。后台创建作为候选保留,待产品接受处理中状态且容量证据支持时复审。

体验目标暂定成功创建请求第 95 百分位耗时不超过 800 毫秒,同时观测成功率、超时率及全部结果分布。可用性目标暂定每月 99.9%,以评审确认的有效请求口径计算。应用部署覆盖独立故障域,验证失去 1 个实例后的容量,并检查数据库与库存依赖的故障影响。一般数据灾难暂以 RTO 30 分钟、RPO 5 分钟演练;已确认订单的数据保护要求须由业务另行确认。

月资源预算暂定 6000 元,估算覆盖计算、数据库、存储、网络、日志和备份,人员工作单独记录。上线验收包括正常创建、并发重复提交、响应丢失、库存超时、实例退出和恢复演练。库存容量、预留残留与数据库恢复列为主要风险,分别配置负责人、告警信号、处置步骤和剩余风险说明。验收记录与 ADR 共同作为上线评审证据。

10 小结:检查清单

提交方案前,用下面的检查项复述你的设计:

自测时可以解释:为什么 3 个实例的正常容量足够,却未必满足活动期间的故障容量目标;为什么创建超时后不能立刻生成新请求标识;为什么增加应用实例不能自动解决库存瓶颈。能够沿着同一条订单链路说清这些关系,就具备了开始评审入门方案的基础。

下一课转向 P13 容灾架构:把本章验收过的结论带进后续主题,写成可检查的方案约束。

同阶段相关课

AI 答疑

默认关闭。启用后,只发送本课标题、最近阅读的屏幕内正文摘录(最多 6000 字)与本次问题。

配置自己的服务