系统流程图栏目 ·

接口调用流程图怎么画:重试、超时、幂等、补偿的画法

接口调用流程图怎么画:把一次“服务 A 调用服务 B”的主流程与异常分支(超时、重试、熔断、幂等、补偿、降级)画清楚,便于研发/产品/测试一起评审与落地,并提供在线生成与导出 draw.io/PNG 入口。

一次看似简单的 HTTP/RPC 调用,真正上线后常常会变成事故的发源地:

  • B 服务偶尔慢 2 秒 → A 服务线程被占满 → 整体雪崩
  • 网络抖动导致超时 → 你加了重试 → 反而把 B 打挂
  • 回调重复、消息重复 → 你以为“再执行一次没事” → 账务出现重复扣款
  • 失败后怎么补 → 你写了补偿脚本 → 但没人知道什么时候该跑、跑几次、以谁为准

所以“接口调用流程图”不是画给学生看的练习题,而是用来对齐可靠性设计的交付物:让研发/产品/测试/运维在同一张图上确认——遇到慢、错、断、重复时系统到底怎么做。

你可以先用文本把节点和连线写出来,让工具自动排版,然后导出 PNG 或 draw.io 贴进 PRD、技术方案或评审纪要:

下面我按“先定边界 → 画主干 → 再补四类异常(超时/重试/幂等/补偿) → 最后加观测与验收清单”的顺序,把这张流程图怎么画讲透。


0. 这张流程图要解决什么:别从符号开始,从问题开始

接口调用相关的流程图,常见有两种失败画法:

  • 把代码画成图:满屏细节,所有 if/else 都上墙,最后没人敢改
  • 把口号画成图:只有“调用/返回/失败重试”,出了问题仍然要靠猜

靠谱的目标是:

  1. 让读图的人一眼看出 “成功路径”需要满足哪些条件(响应时间、返回码、业务校验)
  2. 明确 “慢/错/断/重复”四类异常分别怎么处理(谁负责、重试几次、何时降级、如何补偿)
  3. 给出 可验证的验收点(指标、日志、告警、压测、演练)

你在画之前先回答一句话:这张图是给谁用来做什么决策的?

  • 给研发:确认超时、重试、幂等、补偿的实现边界
  • 给测试:确认需要覆盖的异常注入用例
  • 给产品/运营:确认失败时用户体验(提示、重试、延迟一致性)
  • 给运维:确认告警阈值、降级开关、演练脚本

只要这句话清楚,你的图就不会跑偏。


1. 画图第一步:先把调用边界写在图上(谁调用谁?同步还是异步?)

在接口调用里,“边界”通常比“画得漂亮”更重要。建议你在图的左上角用注释写清楚 6 件事:

  • 调用方:Service A / 网关 / 任务调度器 / 前端?
  • 被调方:Service B / 第三方支付 / 短信服务?
  • 协议:HTTP / gRPC / MQ(同步请求还是异步消息)
  • 一致性目标:强一致(必须马上成功)还是最终一致(允许稍后补齐)
  • 幂等语义:同一请求重复到达,结果是否应该完全相同?
  • 用户感知:调用失败用户能否重试?会不会重复提交?

为什么要写?因为后面的“超时/重试/补偿”都依赖这些前提:

  • 同步调用 + 用户等待 → 超时必须短、重试必须谨慎
  • 异步调用 + 后台处理 → 可以更长的 deadline,但要把补偿、回查画出来
  • 第三方接口不可控 → 你必须把熔断、限流、降级写清楚

2. 主干怎么画:把成功路径压缩成 7~9 个节点

接口调用流程图的“骨架”建议控制在 7~9 个节点(太少会漏关键信息,太多会看不懂)。一个通用的主干结构是:

  1. 接收请求(API / 任务触发)
  2. 参数校验与鉴权(失败直接返回)
  3. 生成请求标识(RequestId/TraceId/IdempotencyKey)
  4. 查询/锁定本地状态(可选:去重、并发控制)
  5. 调用下游(带超时/重试策略)
  6. 处理响应(业务校验 + 映射状态)
  7. 持久化结果(事务边界写清楚)
  8. 返回/通知(同步返回或异步回调)
  9. 记录观测数据(日志/指标/埋点)

这里有两个画图技巧:

  • 把“技术性节点”写成可检查的动作:不要写“处理”,要写“校验返回码并解析业务码”
  • 把“状态写入”画成明确的落点:比如“写订单状态=已支付”,“写调用记录=成功/失败”

你可以把“观测”节点画在主干旁边作为并行动作(不阻塞主流程),这样不干扰阅读。


3. 超时怎么画:用“deadline”而不是“等到对方想回就回”

很多团队画接口调用时只画“调用 B”一个框,但真正的关键是:你愿意等多久?

3.1 三层超时(建议在图里分别标注)

  • 整体超时(End-to-End deadline):从用户请求进入到返回的总预算
  • 单次调用超时(Per-try timeout):每次请求下游等待多久
  • 内部等待超时:队列排队、锁等待、DB 查询、线程池饱和等

画法建议:在“调用下游”节点旁边写注释,例如:

  • deadline=800ms(整体预算)
  • per_try_timeout=200ms(单次)
  • max_attempts=3(最多尝试次数)

注意:per_try_timeout × max_attempts 不等于真实耗时,因为还要加上退避(backoff)、排队和序列化时间。流程图里最好把“退避等待”画成一个小节点,否则大家会误以为重试是“立刻重试”。

3.2 超时后你到底做什么?(这要画成分支)

超时不是“失败”这么简单,它是“你不知道对方到底做没做”。因此超时后的分支至少要包含一种“确认/回查”机制:

  • 立即返回失败(用户可重试)
  • 返回“处理中”(后续异步通知)
  • 进入回查(query)或补偿队列(例如支付/扣库存)

如果你的业务不能接受不确定性(比如扣款),那你必须在图里画出“回查/对账/补偿”的闭环,否则这张图不具备交付价值。


4. 重试怎么画:把“何时重试、重试几次、怎么退避”写到图里

重试是双刃剑:它既能提高成功率,也能把故障放大。

4.1 先在图里区分两种失败:可重试 vs 不可重试

建议你在“处理响应”后面加一个判断节点:

  • 可重试(Retryable):网络错误、连接重置、超时、对方 5xx、限流(看策略)
  • 不可重试(Non-retryable):参数错误、鉴权失败、业务明确拒绝(例如余额不足)

并在判断节点的注释中写出规则:

  • HTTP 429/503:走退避重试(但要有上限)
  • HTTP 400/401/403:直接失败,不重试
  • 业务码 INSUFFICIENT_BALANCE:直接失败

这一步能把很多争论提前终结:不是“失败就重试”,而是“只有明确可重试的失败才重试”。

4.2 重试策略建议写 4 个参数(画在图上)

  • 最大尝试次数max_attempts=2~3(同步链路通常更小)
  • 指数退避backoff=100ms * 2^n
  • 抖动(jitter):随机化退避,避免齐刷刷重试
  • 重试预算(retry budget):在单位时间内最多允许多少重试比例,避免把下游打穿

画法:在“重试等待”节点写 sleep(backoff+jitter),并从“判断可重试”连回“调用下游”。

4.3 反例(建议在图旁边写一句提醒)

  • 不要“无限重试”
  • 不要“立即重试”
  • 不要“把业务失败当成重试对象”

这三条如果不写,后面很容易被“临时修 bug”的人悄悄改成事故。


5. 熔断、限流、降级怎么画:把系统从“硬抗”变成“可控失败”

当下游真的不稳定时,重试解决不了问题,反而会把问题放大。此时你需要在流程图里把“保护机制”画出来。

5.1 熔断(Circuit Breaker)

熔断的关键不是实现细节,而是三件事:

  • 什么时候打开:错误率/超时率达到阈值,或 P99 延迟超过阈值
  • 打开后怎么做:直接失败?走降级?走缓存?
  • 什么时候半开:多久探测一次,探测失败继续打开

画法:在“调用下游”前面加一个判断节点:

  • circuit_open? 是 → 走降级/返回兜底
  • 否 → 继续调用

并在旁边注释阈值(即使是暂定值也写出来)。

5.2 限流(Rate Limit)与隔离(Bulkhead)

  • 限流:防止请求量把自己或下游打挂(令牌桶/漏桶)
  • 隔离:防止一个下游拖垮全局(专用线程池/连接池/队列)

画法:把“限流/隔离”放在调用链路靠前的位置,失败后走一个清晰的分支:

  • 同步接口:返回“系统繁忙,请稍后重试”(可带重试间隔)
  • 异步任务:进入延迟队列或丢弃(写清楚丢弃策略)

重要:限流失败一般不要立刻重试,否则会形成自我放大。


6. 幂等怎么画:别只写“幂等”,要写“用什么键、存在哪、有效期多久”

幂等是接口调用流程图里最容易被“写成口号”的部分。你画图时必须把下面三点落成可执行的动作。

6.1 幂等解决的不是“重复请求”,而是“重复副作用”

  • 重复请求不可避免:用户连点、前端重试、网关重放、MQ 至少一次投递
  • 你要保证:重复到达不会导致重复扣款/重复创建/重复发货

6.2 幂等键(Idempotency Key)怎么来的?

常见来源(选一种并写在图里):

  • 客户端生成(更适合“创建类”接口),服务端校验格式
  • 服务端生成(返回给客户端,下次带上)
  • 业务唯一标识复用(例如订单号)——但要确保不会复用到不同语义

画法:在主干第 3 步明确写“生成/获取 IdempotencyKey”,并在后面加一个判断:

  • key 是否已处理? 是 → 直接返回历史结果(或返回同样状态)
  • 否 → 继续执行,并在成功后写入“已处理记录”

6.3 幂等记录存哪里?有效期多久?

这一步决定了幂等是否可靠:

  • 存 DB:可靠但可能增加写压力;适合关键交易
  • 存 Redis:快但要考虑过期、持久化与重启丢失;适合短窗口幂等

流程图里建议写一句注释:

  • 幂等记录 TTL=24h(或按业务)
  • 返回结果缓存:成功/失败是否都缓存?(通常只缓存“已成功”或“可复用结果”)

如果你不写 TTL,后面就会出现“Redis 过期后重复扣款”的经典事故。


7. 补偿怎么画:把“失败后的世界”画成闭环,而不是一条断头路

补偿(Compensation)是接口调用流程图的“交付价值放大器”。你只要补上这一块,图的可用性会立刻从“看懂”变成“能落地”。

7.1 先决定一致性策略:同步强一致还是异步最终一致?

  • 强一致:要么全部成功,要么全部失败回滚(通常要求同库事务或分布式事务)
  • 最终一致:允许先记录意图/中间态,后续通过异步补齐或回查

现实里多数跨服务调用都走最终一致。那你必须在图里画出:

  • 中间态(例如“处理中/待确认/待回查”)
  • 触发补偿的条件(超时、不确定、下游失败)
  • 补偿的执行者(定时任务/消息消费者/人工触发)

7.2 推荐画两条“补偿闭环”分支

闭环 A:回查(Query/Confirm)

适用于“对方可能已成功但你没收到响应”的场景(典型:支付、发券、外部系统)。画法:

  • 超时/未知结果 → 写入“待回查”记录 → 进入回查队列
  • 回查成功 → 更新状态为成功并触发后续
  • 回查失败/不存在 → 进入补偿或终止

闭环 B:反向补偿(Saga)

适用于“你已经执行了本地副作用,但后续步骤失败”的场景。画法:

  • 步骤 1 成功(本地落库)
  • 步骤 2 调用下游失败
  • 进入补偿:执行反向动作(释放库存/撤销预占/冲正)

关键要求:补偿动作也要幂等。流程图里最好在补偿节点旁边写一句“补偿幂等/可重入”。

7.3 Outbox(事务外发)如何在图里表达

很多团队的痛点是:DB 事务提交了,但消息没发出去;或者消息发了,但 DB 没提交。

如果你用 Outbox 模式,画法通常是:

  • 本地事务:写业务表 + 写 outbox 表(同一事务)
  • 异步投递器:扫描 outbox → 投递 MQ → 标记已发送
  • 消费者:幂等消费

你不必在本文里画到字段级,但至少要把“写 outbox”与“异步投递”两个节点画出来,并标注它们与事务的关系。


8. 观测与可运维怎么画:让这张图能指导告警与排障

接口调用最怕的是“失败了,但你不知道在哪失败”。所以流程图里至少要让人看到下面三类观测点:

8.1 日志:每个请求至少要能串起来

建议在图旁边写明:

  • TraceId/RequestId 全链路透传
  • 关键节点记录:开始/下游调用/返回/重试次数/最终结论
  • 错误分类:超时、连接失败、返回码异常、业务码异常

8.2 指标:让你能回答“现在到底坏到什么程度”

最常用的指标组合:

  • 调用成功率(按下游、按接口)
  • 延迟分位数(P50/P90/P99)
  • 超时率、重试率、熔断打开次数
  • 限流拒绝数、降级命中率

8.3 告警与演练:把“恢复路径”写出来

  • 告警:错误率/延迟/熔断打开、重试率异常
  • 处置:降级开关、限流阈值调整、回查/补偿任务手工触发
  • 演练:定期做超时注入、下游 5xx、网络抖动、重复消息

把“手工处置入口”写在图里很重要:因为事故时没人想翻文档。


9. 你可以照着抄的 1 个小例子(仅用于理解,不是模板库)

下面是一个“创建订单后调用支付预下单”的极简文本流程(只为帮助你理解节点如何组织;不要直接当作你业务的最终版本):

  1. 接收创建请求
  2. 校验参数/鉴权
  3. 生成 IdempotencyKey(客户端或服务端)
  4. 查询幂等记录:已成功 → 返回历史结果
  5. 本地事务:创建订单=处理中 + 写 outbox(发起支付)
  6. 投递 outbox → MQ(失败重试,幂等)
  7. 消费者调用支付服务(per_try_timeout=200ms, max_attempts=2, backoff+jitter)
  8. 超时/未知 → 写待回查;明确失败 → 更新订单失败
  9. 回查确认成功 → 更新订单成功并通知用户

你把这些行复制到文本生成工具里,再补充判断条件(成功/失败/超时)和异常分支,就能得到一张可评审的流程图:


10. 交付级“接口调用流程图”检查清单(画完对照一遍)

把下面清单当成验收标准:你能在图里找到对应答案,这张图才算“能落地”。

10.1 基础信息

  • 调用方/被调方/协议是否写清楚
  • 同步还是异步?用户是否等待?
  • 成功的业务判定条件是什么(不止 HTTP 200)

10.2 超时与重试

  • end-to-end deadline 是否明确
  • per-try timeout 是否明确
  • 重试次数、退避、抖动是否明确
  • 哪些错误可重试,哪些不可重试
  • 是否有重试预算/限流避免放大故障

10.3 幂等与去重

  • 幂等键来源与格式
  • 幂等记录存储位置与 TTL
  • “重复请求”返回什么(历史结果/相同状态/处理中)

10.4 保护与降级

  • 熔断触发条件与打开后的行为
  • 限流失败后的用户体验/任务策略
  • 资源隔离(线程池/连接池)是否考虑

10.5 补偿与闭环

  • 超时/未知结果如何回查确认
  • 失败后的补偿动作是什么、谁执行
  • 补偿动作是否幂等、可重入
  • 是否有对账/修复的最终落点(报表/归档/状态一致)

10.6 观测与运维

  • TraceId/RequestId 是否全链路贯通
  • 指标与告警阈值是否可落地
  • 事故时的手工处置入口是否明确

如果你打算把这张图发给同事评审,我建议你把“清单”也贴在图旁边:评审时就不会陷入纯主观讨论。


11. FAQ:几个最常见的争议点(建议在评审会上直接对齐)

Q1:超时了到底算失败还是算未知?

超时的本质是“你不确定对方是否已经处理”。

  • 对“无副作用查询类”接口:超时可以当失败(用户重试即可)
  • 对“有副作用写入类”接口:超时更像未知,需要回查/幂等/补偿闭环

所以流程图里不要只画“超时→失败”,至少要画“超时→回查/处理中”。

Q2:重试应该在客户端做还是服务端做?

  • 客户端重试:能提升用户成功率,但可能导致重复提交(必须配合幂等键)
  • 服务端重试:更可控,但会占用服务端资源(必须控制预算与退避)

通常建议:客户端做一次轻量重试 + 服务端做有限重试,并把幂等作为前置条件。

Q3:为什么加了重试反而更慢、更不稳定?

因为你把“失败”变成了“更多请求”,在下游变慢时会进一步加压。解决方向在流程图里应体现:

  • 重试只针对可重试错误
  • 带退避与抖动
  • 有熔断与限流
  • 有重试预算

Q4:幂等到底是“去重”还是“保证结果一致”?

更准确的说法是:保证副作用不重复

  • “去重”只是手段(记录 key)
  • 目标是:同一语义的请求重复到达,不会造成重复扣款/重复创建

Q5:补偿是不是等于“写个脚本修数据”?

不是。补偿应该是流程的一部分:

  • 有明确触发条件
  • 有可重复执行(幂等)
  • 有最终落点(状态一致/归档)

脚本只是补偿的其中一种执行形态。


12. 你下一步可以怎么做(把图从“会画”变成“能用”)

  1. 先按本文的骨架把主干画出来(7~9 个节点)
  2. 只补四类关键异常:超时、重试、幂等、补偿
  3. 把清单贴上,开一次 30 分钟评审会
  4. 根据评审结论把阈值、TTL、重试次数写死在图里(哪怕是暂定)

如果你希望把文字节点快速变成结构清晰的流程图,并一键导出 draw.io/PNG 给同事评审,可以直接用:

这类“接口调用”流程图,画得越早越省钱:你在图上把分支写清楚,往往就能少掉一半线上事故和无意义的扯皮。