P10

云上参考架构入门

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

课内目录

1 为什么先学「云上参考架构入门」

零售小店准备把订单 API 部署到云上。顾客点击“提交订单”,系统需要确认身份、检查商品、记录订单,再返回结果。业务同事关心顾客能否下单,开发者关心接口是否正确,运维人员关心服务故障后能否恢复。参考架构把这些问题放到同 1 张地图上:请求经过哪些部件,数据存在哪里,哪个部件失败会影响谁,以及哪些证据能够解释结果。

参考架构是适用于某类问题的结构范例,包含组件角色、连接关系、责任边界和成立条件。它不是照着购买的资源清单,也不能代替需求分析。画上负载均衡、数据库和消息队列,并不代表系统已经可靠。我们必须说明为什么需要这些角色、它们如何配合,以及业务可以接受怎样的等待和失败。不同云平台的产品名可以变化,这些问题仍然存在。

本课采用 1 个持续场景:零售订单 API 提供创建订单和查询订单的能力。初期业务允许库存确认稍晚完成,但不能把同 1 次购买变成 2 笔订单;创建成功的订单应当可以查询;促销时流量会增加。这里先把支付看成外部依赖,不展开支付合规和资金对账。这样的边界让我们能练习架构推理,又不会把入门课变成完整电商平台设计。

学习目标是读懂并修改参考图,而不是记住某种标准答案。看到 1 条箭头,要能追问它代表同步请求、异步消息还是管理操作;看到 2 个实例,要能追问它们是否落在相互独立的故障范围;看到缓存,要能追问缓存失效后正确性由谁保证。能够回答这些问题,才有依据判断图是否适合自己的订单系统。

订单 API 参考架构与责任边界
订单 API 参考架构与责任边界

图中从顾客到订单 API 的路径属于处理业务请求的数据面。创建资源、配置权限和发布版本属于管理系统的控制面。控制面故障可能暂时妨碍扩容,却不必立即中断已有实例处理请求;数据面故障则可能直接影响下单。把这 2 类路径分清,可以避免把“控制台打不开”直接等同于“业务全部不可用”。实际影响仍要从业务请求与监控证据确认。

2 核心概念与边界

先按角色理解组件。入口负责接收连接、分发请求,并执行约定的流量限制;应用负责订单规则;数据库保存订单的权威状态;缓存保存可重新生成的副本;队列保存等待处理的任务或事件。角色与产品并非严格对应,1 个产品也可能承担多个角色。入门阶段应先把角色讲清楚,再根据规模、团队能力和成本选择具体实现。

同步调用意味着调用者等待结果再继续。创建订单时,应用通常需要等待数据库确认写入,才能返回“已受理”。异步处理意味着先记录后续工作,再由消费者独立执行,例如通知顾客或更新搜索视图。异步可以削平短时流量,却会带来等待、重复投递和处理失败。它改变了完成时刻和故障处理方法,不会凭空增加下游的持续处理能力。

状态是影响下一次处理结果、需要保存的信息。订单记录属于状态;某个应用实例内临时计算出的折扣结果可能只是短暂数据。无状态应用的意思是,请求正确处理所需的持久信息不只放在这个实例里。这样入口才能把下一次请求送到其他实例。登录状态、幂等记录和上传进度若藏在本机内存中,增加实例后可能出现间歇性错误。

网络边界回答谁能够访问谁。虚拟私有网络提供逻辑隔离的网络范围,安全组等访问规则控制允许的连接。把数据库放进私有网络通常能减少直接暴露,但应用仍需认证、授权和参数校验。网络可达不等于业务有权访问。订单 API 还要验证用户只能查询自己的订单,应用访问数据库也应使用完成任务所需的最小权限。

故障域是可能共同失效的范围,例如 1 台主机、1 个可用区或 1 个区域。可用区可以理解为区域内相对独立的基础设施位置。跨可用区放置应用实例,可以降低某些共同故障的影响,但如果它们依赖同 1 个不可恢复的数据库,订单写入仍会中断。冗余应沿着业务必经路径检查,不能只数应用实例数量。

还要区分可用性与可恢复性。可用性关注需要服务时能否得到符合约定的结果;可恢复性关注故障后如何恢复数据和服务。备份不承担实时请求,却能帮助恢复误删除的数据;副本可以改善部分故障下的连续服务,却可能同步传播错误修改。2 者需要分别设计。恢复点目标表示最多可接受丢失多长时间的数据,恢复时间目标表示希望在多长时间内恢复业务,两者都应由业务需求确定。

3 关键机制(可迁移)

第 1 个机制是分工与隔离。入口把请求交给多个应用实例,应用把持久状态交给数据库,耗时的后续工作交给后台消费者。分工让每类组件可以单独调整容量,也让故障影响有机会被限制。但每增加 1 个远程依赖,就增加了延迟和失败机会。只有当职责、容量或故障隔离确有需要时,拆分才有收益。小型系统完全可以从清晰的单体应用起步。

第 2 个机制是健康检查与流量切换。入口定期检查应用,发现实例不能处理请求后停止给它发送新流量。检查项要与接收流量的能力相关:进程存在不代表订单接口可用;把每个可选依赖都作为失败条件,又可能在通知服务故障时摘除全部应用。可以分别表达进程是否存活、实例是否就绪,并记录被摘除的原因。流量切换还存在探测与连接排空的时间窗口。

第 3 个机制是超时与有限重试。调用者不应无限等待数据库或外部服务。超时应当放进整条请求的时间预算里,给响应和清理留出余量。重试只适合可能暂时恢复的错误,并应限制次数、留出间隔,必要时加入随机变化。若入口、应用和客户端各自重试 3 次,实际下游调用可能被层层放大,故障期间反而更难恢复。

第 4 个机制是幂等,也就是同 1 个业务动作重复提交时,效果符合约定。客户端为 1 次购买生成请求键,超时重发时保留这个键。服务端按用户和键查找已受理的操作,并用数据库唯一约束等机制防止并发创建重复订单。相同键若携带不同商品或金额,应明确拒绝。键的保存期限也要覆盖业务允许的重试窗口,不能只依赖短暂的进程内缓存。

第 5 个机制是背压。当请求进入速度持续超过处理速度,系统应限制新工作,而不是让所有连接和队列无限增长。入口可以返回明确的限流响应,消费者可以限制并发,数据库连接池可以限制占用。背压会让部分请求等待或被拒绝,但有助于保住已接受工作。拒绝策略必须与客户端约定,否则客户端立即重试,仍可能形成更大的拥塞。

交互把入口分发、订单落库、提交后响应和后台处理依次点亮。观察每个阶段保留了什么证据,以及哪里能够安全重试。这里“提交后响应”表示订单已经达到约定的持久化条件,并不表示所有后台工作都结束。用户界面应展示“已受理”或“待确认”等准确状态,避免把技术步骤提前翻译成业务完成。

4 关键机制续:落到订单链路

现在把机制连成请求路径。客户端发送创建订单请求,入口完成路由,应用检查身份、字段与幂等键,再向数据库提交订单。对于库存尚未确认的业务,初始状态可以是“待确认”。数据库确认事务提交后,应用返回订单编号。客户端随后根据编号查询状态。这个约定使“受理成功”成为可观察的节点,也使后续库存失败有明确的业务处理空间。

创建订单与异步确认的处理顺序
创建订单与异步确认的处理顺序

订单写入与发送消息之间存在容易忽略的空隙。先写数据库、再发送消息,如果进程在中间退出,就可能留下没有后续处理的订单;先发消息、再写订单,消费者可能找不到订单。入门时可以理解事务发件箱的思路:在同 1 个本地数据库事务里写订单和待发送记录,后台再把记录送入队列。它让已提交的后续工作可以被发现和补发,但仍要处理重复发送。

消费者收到消息后执行库存确认,并按业务规则更新订单状态。只有处理结果达到约定的持久化条件后,才确认消息已处理。确认之前退出,消息可能再次到达,因此消费者也需要幂等。例如以订单编号和动作类型记录是否处理过,用合法状态迁移避免重复扣减。队列提供的投递语义与业务效果是 2 件事,不能仅凭“有队列”就承诺每笔业务只执行 1 次。

对于持续处理失败的消息,应设置有限重试与隔离去向,并保留失败原因、尝试次数和订单编号。隔离后的消息需要负责人和补偿流程;把它移走并不代表问题解决。若库存不足属于正常业务结果,就应将订单转成相应状态并通知顾客,而不是当作技术错误无限重试。区分业务拒绝与系统故障,会直接影响告警质量和排障效率。

查询路径也需要一致性约定。刚创建的订单若立即从延迟同步的副本读取,可能暂时查不到。可以让创建后的关键查询读取权威写入端,或明确提供带有更新时间的处理中状态。缓存中的商品展示信息允许短暂陈旧,不代表库存扣减也可以依赖同样的陈旧副本。应当逐类说明数据需要多新,而不是给整个系统贴上“强一致”或“最终一致”的标签。

弹性扩容只对能够增加的处理能力有效。如果每个订单都要占用数据库连接,应用实例翻倍可能先耗尽连接数。队列消费者增加也可能压垮库存依赖。扩容前要识别瓶颈,设置每个实例的并发上限,并确认下游余量。容量单位可以是每秒请求数、同时处理数或每秒完成任务数,但比较时必须采用相同条件和口径。

滑块用简化模型展示“应用处理槽位增加,但共享数据库能力不变”的结果。随着槽位增加,示意吞吐先上升,再受共享上限限制;继续增加只会带来更多占用和成本。它帮助建立瓶颈直觉,不提供生产容量承诺。实际系统还受到查询复杂度、热点数据、网络和故障恢复速度影响,需要用符合订单负载特征的压测确认。

5 动手与命令/控制台直觉

本节用接口交互建立直觉,不要求创建云资源。假设练习环境已经提供订单 API,使用占位地址替换实际地址即可。提交前先确认测试用户、商品和环境,避免把练习请求发到真实交易系统。下面的请求键代表同 1 次购买意图;如果用户重新发起另 1 次购买,应使用新的键。

curl -i -X POST 'https://orders.example.test/orders' \
  -H 'Authorization: Bearer <test-token>' \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: demo-order-1001' \
  -d '{"sku":"SKU-101","quantity":1}'

重点看响应的含义,而不是只看状态码是否属于成功范围。接口可以约定返回 201 表示订单资源已创建,正文中包含订单编号和 pending 状态;也可以采用其他清楚的受理约定。HTTP 状态码表达请求层面的结果,订单状态表达业务进度。应在接口文档里把这 2 层分开解释,避免客户端看到成功响应就显示“库存确认完成”。

用相同请求键与相同内容再次提交,期望拿到同 1 个订单编号,而不是新增订单。再用同 1 个键提交不同数量,期望得到明确冲突响应;具体状态码由接口契约规定。还可以模拟响应丢失:服务端可能已经提交,只是客户端没有收到结果。此时正确做法是按约定查询或用原键重试,不能把超时直接解释为没有下单。

curl -i 'https://orders.example.test/orders/ord-1001' \
  -H 'Authorization: Bearer <test-token>'

在控制台中,应把图里的角色逐个对应到资源与配置。入口查看后端实例和健康检查;应用查看实例位置、版本及并发限制;数据库查看连接数、备份和恢复设置;队列查看积压、最老消息年龄和失败消息。先读配置与指标,再决定修改。某项开关名为“高可用”,仍要确认它覆盖哪些故障、切换多久以及应用是否需要重连。

可以在隔离练习环境中观察 1 个应用实例退出后,健康检查何时变化、剩余实例何时接到流量、请求是否出现短暂失败。实验前写下预计结果和恢复操作,实验后对照时间线。不要用停止生产数据库来验证入门概念。对无法安全演练的依赖,先讨论故障假设与证据需求,再安排受控演练。

6 观测:看哪些字段与信号

观测的起点是业务问题:顾客是否能完成下单,已受理订单是否继续推进。指标呈现整体趋势,日志保留具体事件,追踪连接 1 次请求经过的多个组件。3 类证据相互补充。只看到 CPU 不高,无法说明订单 API 正常;大量请求也可能正在等待数据库连接,或者入口已经拒绝了它们。

订单链路的指标、日志与追踪关联
订单链路的指标、日志与追踪关联

先定义统计口径。订单创建请求总量、成功量、技术失败量、业务拒绝量和响应时间应分别观察。业务拒绝例如库存不足,并不必然属于服务不可用。延迟可以观察中位数和较高分位数,例如 P95 表示约 95% 的样本耗时不超过该值。平均值容易掩盖少数很慢的请求;分位数也必须说明时间窗口和采样范围。

日志建议包含时间、服务名、版本、请求标识、追踪标识、订单编号、结果类别和耗时。跨组件记录要使用可关联的标识,并保持时钟同步。不要直接记录令牌、完整地址或其他敏感内容。幂等键也应按数据管理要求脱敏或摘要化。日志字段的目的在于定位问题,不是把请求正文全部复制到另 1 个存储系统。

数据库侧关注连接使用量、等待时间、慢查询、事务失败和存储余量;队列侧关注进入速度、完成速度、积压量及最老消息年龄。积压量稳定不一定健康,因为消息可能已等待很久;消息量很少也不一定正常,因为生产者可能没有成功发送。把发件箱未发送记录与队列消费进度一起看,才能覆盖提交到处理之间的空隙。

告警应对应可行动的问题。例如“待确认订单的年龄超过业务阈值”可以提示后台处理停滞,“创建订单技术失败率升高”可以提示请求链路异常。阈值和持续时间需要结合业务节奏,避免促销开始后的正常增长触发大量无效告警。每条告警都应能回答由谁处理、先看哪里、什么条件下算恢复。

观测还有成本与可靠性边界。每条请求都打印大量日志会增加开销,采样又可能漏掉个别事件。因此订单权威记录、幂等记录和必要的审计证据不能完全依赖采样追踪。监控系统自身故障时,应有办法区分“没有错误”与“没有收到数据”。图里画出的观测链路也需要最基本的健康检查和数据保留约定。

7 常见坑与反模式

常见的第 1 类问题是先堆组件,再寻找用途。订单量很小就拆出大量服务,会增加发布、权限、日志关联和远程调用的负担。参考架构中的可选组件,应当写明触发条件:只有确实存在耗时后续任务时才引入队列,只有可测的读取压力或延迟需求时才引入缓存。简单结构只要职责清楚、恢复路径明确,也能成为合适的起点。

第 2 类问题是把复制当成备份,把多实例当成完整容灾。误删订单可能很快复制到其他副本;应用跨可用区也不能弥补同 1 个依赖的全部故障。应当分别回答实时服务如何继续、数据如何找回、恢复后如何确认正确。备份存在但从未恢复过,只能证明生成过文件,无法证明满足业务恢复目标。

第 3 类问题是把缓存和队列当成无限缓冲区。缓存失效会让请求集中进入数据库,长时间积压会让订单状态迟迟不更新。需要提前定义容量上限、失效行为和降级方式。例如商品推荐不可用时可以隐藏推荐区域,但创建订单不能在未持久化的情况下返回受理成功。降级必须保留关键业务语义。

第 4 类问题是为了“安全”阻断所有故障信息。给客户端返回简洁错误是合理的,内部却必须保留可定位的错误类别和关联标识。反过来,直接把数据库报错、内部地址和令牌返回给用户也不合适。对外提供稳定的错误契约,对内保留受权限控制的诊断证据,才能同时支持用户体验和故障处理。

第 5 类问题是只考虑正常路径的成本。空闲副本、跨区流量、备份、日志、队列存储和恢复演练都可能产生费用。初期无需精确预测每笔支出,但应说明主要费用随什么增长。比较方案时,应把达到同样业务目标所需的容量和运维工作放在一起看,而不是只比较 1 台应用实例的价格。

8 案例:订单系统相关故障

促销开始后,创建订单的 P95 延迟升高,部分客户端超时。值班人员发现应用 CPU 并不高,于是把实例数从 4 增加到 8。短时间内错误更多,数据库连接等待明显上升。这里 CPU 低的原因可能是应用在等待连接,新增实例又带来了更多竞争。扩容没有改变共享数据库的能力,反而扩大了同时等待的请求数量。

排查人员先按入口请求量、应用并发和数据库等待对齐时间线,确认连接等待早于错误率上升;随后检查近期发布,发现某个查询缺少合适的访问路径,持有连接的时间变长。处置时先限制入口流量和应用并发,减少无效重试,再按已验证的发布流程修复查询。回退是否可行还取决于数据结构与版本兼容性,不能把回退当作始终安全的按钮。

另 1 个现象是部分订单一直停在“待确认”。创建接口已经恢复,顾客仍不断刷新。队列最老消息年龄升高,消费者日志显示访问库存服务持续超时。此时只看入口成功率,会误判系统已完全恢复。处理人员降低消费者对故障依赖的调用压力,并保留待处理任务;依赖恢复后受控追赶积压,同时确认等待过久的订单是否需要业务补偿。

交互展示响应丢失后的重复提交。第 1 次请求已经提交订单和发件箱记录,网络却在响应返回前中断。客户端用原请求键重试,应用返回已有订单;后台发送可能重复的事件,消费者通过处理记录维持约定效果。每个阶段都标明“不确定”的观察视角,帮助理解客户端超时与服务端未执行之间不能直接画等号。

这类故障的恢复判据应包含前台与后台:创建成功率和延迟回到约定范围,最老消息年龄持续下降,异常订单得到核对,重复提交没有产生额外订单。还要确认降流或临时配置已经按计划恢复。故障后的改进可以是查询优化、并发预算、幂等约束和积压告警;无需因为发生过故障,就立即升级成更复杂的跨区域架构。

9 排查案例:证据链怎么写

证据链应让没有参与现场的人理解结论怎样得出。先写影响,再写事实与假设,最后写操作和结果。例如:“10:05—10:18,创建订单请求出现技术失败,已提交订单记录仍可查询。”这句话交代接口、时间和已知边界。若尚未核对全部订单,就不要写“没有数据损失”,应明确目前检查了哪些记录。

从业务症状到恢复确认的排查证据链
从业务症状到恢复确认的排查证据链

接着把证据按时间关联:10:05 数据库连接等待升高,10:06 应用延迟升高,10:08 扩容后连接竞争加剧。每项记录要附指标窗口、查询条件或相关标识,避免用截图中的相似曲线直接证明因果。可以写出待验证假设:“慢查询延长连接占用,使请求等待。”再用查询耗时、连接池状态和修复后的变化支持或否定它。

处置记录应保留变更对象、时间、操作者和观测结果。例如降低并发后错误率下降,但吞吐也下降,说明系统压力得到缓解,尚不能证明慢查询已经修复。若同时修改多项参数,很难知道哪项起作用。条件允许时采用可回退的小步调整,并持续检查业务影响;紧急情况下必须并行处置,也应如实记录不确定性。

最后写清恢复证据与后续责任。恢复既包括接口指标,也包括积压、订单状态和数据核对。根因结论要与证据强度匹配,不能把“重启后好了”写成确定根因。后续事项应具体到要补的约束、指标或演练及负责人。这样,参考架构就从展示用的图,变成能够连接设计、运行和复盘的共同语言。

放进方案里

在零售订单 API 方案中,先用业务语言写边界:系统提供创建与查询订单,创建成功表示订单已持久化并进入待确认状态,库存确认异步完成,支付由外部系统承担。写出预计请求量、促销峰值和允许等待时间;暂时没有测量的数据要标成假设,并给出验证方式。不要让读者从组件名称猜测业务承诺。

随后附上参考架构图,标明入口、应用、数据库、发件箱、队列和消费者的职责,区分同步请求与异步消息。说明应用实例所在故障域、数据库访问边界和最小权限,并列出共享依赖。缓存若尚无明确需求,可以暂不引入。每个组件都要能对应到业务问题或可验证的运行需求。

对关键语义单独说明:同 1 次购买以请求键识别,服务端用持久化约束防止重复订单;订单和待发送记录在同 1 个本地事务中提交;消费者按订单与动作处理重复消息。约定创建后查询从哪里读取、状态允许延迟多久、失败订单如何通知和补偿。把“可用”“成功”“完成”替换成可以测试的具体条件。

容量与恢复部分列出入口限流、应用并发和数据库连接预算,说明何时扩容、遇到共享瓶颈如何控制压力。给出业务认可的恢复点与恢复时间目标,以及备份恢复的验证安排。列明前台成功率、延迟、最老消息年龄和异常订单数量等信号,设置负责人与升级路径。

最后附验证与取舍:用重复提交、响应丢失、实例退出和消费者暂停验证关键路径;记录主要费用来源与团队维护负担。对尚未完成的压测和恢复演练给出计划,避免写成已具备的能力。让评审者能够沿着“业务要求—设计选择—验证证据”检查方案,参考架构才真正进入了交付过程。

10 小结:检查清单

评审订单 API 参考架构时,可以按以下清单逐项回答:

自测时尝试解释 3 个问题:客户端超时后为什么可能已经下单;应用实例增加后为什么可能更慢;入口成功率恢复后为什么还要检查队列。能够沿着订单状态、资源等待和后台进度回答,就已经掌握了阅读入门参考架构的主要方法。

下一课转向 P11 高可用基础模式:把本章验收过的结论带进后续主题,写成可检查的方案约束。

同阶段相关课

AI 答疑

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

配置自己的服务