故障注入测试
故障注入测试主动在限定范围内制造错误、延迟或资源异常,观察系统是否按预期失败、降级和恢复。它要验证的是故障发生后仍应成立的行为,例如请求有界结束、重复操作不产生重复扣款,以及故障撤除后能够继续服务。
一次实验需要明确注入在哪里、影响哪些对象、持续多久,以及如何确认恢复。只让服务报错,再截一张监控图,无法说明容错机制是否有效。
1. 面试核心
先记住这几条,再读正文- 故障实验先定义正常基线、故障假设、影响范围和停止条件,再关联客户端、服务端与数据证据。卡 · 实验假设与过程
- 返回错误、进程终止和网络异常发生在不同层次,一个替身返回 500 不能证明网络故障处理正确。卡 · 注入位置与证据强度
- 弱网包含延迟、带宽、丢包、抖动和连接变化,仅增加固定延迟不能覆盖所有弱网行为。卡 · 弱网与连接变化
- 超时只说明调用方没有及时得到结果,不证明远端操作没有提交;重试时还需核对副作用。卡 · 提交后的超时与重试
- 恢复验证既检查流量,也检查数据与积压,撤销注入不等于系统已经恢复。卡 · 恢复与回归
2. 实验假设与过程
实验假设应是一条可以被证伪的行为判断。例如:“库存依赖持续超时 10 秒时,下单请求会在约定期限内结束,不重复扣减库存;依赖恢复后,新订单能够完成。”这里的时长和行为是示例约定,实际值由系统目标决定。
把假设变成实验时,需要记录以下内容:
| 内容 | 要回答的问题 |
|---|---|
| 基线 | 无故障时同样负载与数据的结果是什么 |
| 目标 | 哪个测试服务、实例、连接或请求受到影响 |
| 故障 | 类型、强度、方向、持续时间和触发时机 |
| 预期 | 客户端结果、错误率、耗时及业务不变量 |
| 停止条件 | 哪个指标或状态出现后立即结束实验 |
| 恢复方式 | 怎样撤销注入,怎样确认撤销确实生效 |
| 证据 | 请求标识、指标、日志、队列与最终数据 |
先在可隔离的测试范围验证注入和撤销,再逐步增加复杂度。实验本身也可能失败:选择器没有命中目标,代理未经过实际流量,或故障发生在业务早已完成之后。因此,必须先证明预期故障确实发生,才能解释系统反应。
恢复操作应能独立于被测请求执行。若实验控制器失联,仍需要时限、资源归属和补偿清理,避免注入状态无限残留。具体工具的选择器和时限机制需要按实际部署核对。
3. 注入位置与证据强度
不同注入方式适合验证不同机制,不能互相替代。
| 注入位置 | 可以构造 | 不能直接证明 |
|---|---|---|
| 函数或客户端替身 | 异常、指定响应、固定延迟 | 真实网络、序列化和连接池行为 |
| 应用处理逻辑 | 特定阶段返回错误或延迟 | 进程崩溃后的资源和持久化边界 |
| 网络代理或网络层 | 延迟、带宽限制、断连等 | 所有真实设备和运营商网络行为 |
| 进程或实例 | 退出、重启、不可用 | 其他未注入实例或跨区域故障 |
| 存储或资源 | 磁盘、连接、容量等限制 | 未建模的依赖与恢复过程 |
例如应用直接返回 HTTP 500,调用方仍收到了一份完整协议响应;连接在响应返回前中断,则可能没有任何完整响应。前者验证错误码处理,后者还涉及超时、连接释放和结果未知。
选择注入点应贴合要验证的失效路径。只验证页面显示错误时可以用受控响应;验证客户端网络重试时,应让请求经过真实协议栈与对应连接故障。替身的一般边界见测试替身。
4. 弱网与连接变化
弱网测试在不理想的网络条件下检查产品表现。网络条件至少应拆开描述:延迟影响响应等待,带宽限制影响传输时间,丢包和抖动影响交付节奏,断连及网络切换影响连接和状态恢复。
需要考虑故障发生的阶段和方向。例如请求体已经到达服务端后,响应方向断开,与请求根本没有送达是两种状态;上传中途断开和下载完成后断开,也不能使用同一预期。
| 场景 | 重点检查 |
|---|---|
| 建连缓慢或失败 | 连接超时、错误提示、资源释放 |
| 响应慢或抖动 | 总等待期限、取消、加载状态与重试 |
| 上传或下载中断 | 已完成范围、恢复协议与重复写入 |
| 网络恢复或切换 | 重新连接、身份状态及未完成操作处理 |
| 双向条件不对称 | 请求已提交但响应未达时的结果判断 |
可以使用代理模拟 TCP 链路上的部分异常,也可以在具备条件的测试环境使用网络层工具。工具参数需要与目标流量路径一致;仅在浏览器里限制某条 HTTP 请求,不能宣称覆盖应用所有连接。
客户端可用性和业务一致性都需要检查:用户是否看到明确状态、能否取消,重新点击是否重复提交,以及最终服务端记录是什么。超时与取消的机制见超时。
5. 提交后的超时与重试
最容易遗漏的故障窗口之一,是服务端已经完成操作,但响应没有被调用方收到。调用方看见超时后再次发送,可能造成重复副作用。
下面使用内存模型故意在写入后抛出超时,只演示这个窗口,不模拟真实网络或生产存储:
class PaymentModel:
def __init__(self):
self.records = {}
def pay(self, key, amount, lose_response=False):
if key in self.records:
if self.records[key] != amount:
raise ValueError("key reused with different amount")
else:
self.records[key] = amount
if lose_response:
raise TimeoutError("response lost after commit")
return {"key": key, "amount": self.records[key]}
model = PaymentModel()
try:
model.pay("request-1", 100, lose_response=True)
except TimeoutError:
pass
assert model.records == {"request-1": 100}
result = model.pay("request-1", 100)
assert result == {"key": "request-1", "amount": 100}
assert len(model.records) == 1
print("one effect after retry")
输出 one effect after retry。第一次超时前,记录已经写入;第二次使用同一标识返回原结果,没有创建第二条记录。若实现每次都追加副作用,用例就应从最终业务记录发现重复,而不只检查第二次响应成功。
真实实现还需要处理并发、持久化、标识范围、过期和参数冲突,内存字典不能提供这些保证。测试时应分别构造提交前失败、提交后响应丢失、重复到达和相同标识不同参数,机制见幂等键。
重试次数和间隔也应受整体期限约束。依赖异常时观察实际调用次数,避免客户端、网关和服务分别重试后放大流量;相关机制见重试。
6. 恢复与回归
撤除故障后,需要证明服务重新完成业务,而不只是注入工具显示“已结束”。连接可能仍然耗尽,队列可能继续积压,缓存或数据也可能已经进入错误状态。
恢复检查应包括新请求成功、延迟和资源回到可接受范围、积压得到处理,以及故障期间任务的最终状态。对重复、丢失和补偿失败要按业务标识核对,不能仅查看总记录数。
实验发现的问题应保留可重复条件和最小回归方式。能在小范围稳定表达的错误逻辑,可以增加单元或接口回归;依赖真实故障窗口的问题,仍需保留相应集成实验。报告分别说明注入成功、故障期间行为和恢复结果,避免用单一“通过”掩盖某个阶段没有证据。
7 · 面试题卡片
本页题目与参考回答
7.1 怎样说明一个故障注入或混沌测试项目?
考察重点:可证伪假设、影响范围、观测与恢复。
参考回答
从要验证的行为假设出发,说明基线、注入对象和故障、持续时间、停止条件及恢复方式。 先证明注入命中实际路径,再用客户端、服务端与数据证据判断系统反应。
结果分别说明故障期间是否满足期限和业务不变量,撤除后是否恢复,以及发现的问题怎样复现和回归。只展示故障截图不能说明容错机制有效。
7.2 用 mock 返回 500,能否替代真实连接中断测试?
考察重点:注入层次与实际失效路径。
相关内容:注入位置与证据强度。
参考回答
不能,完整 500 响应与连接中断提供了不同信息,也经过不同处理路径。 mock 适合检查给定错误下的逻辑,真实连接中断还涉及协议、超时、资源释放和结果未知。
根据目标机制选择注入位置,并明确结论范围。验证真实客户端网络行为时,需要让请求经过对应协议栈和连接故障。
7.3 怎样设计弱网测试?
考察重点:网络维度、故障时机和业务状态。
相关内容:弱网与连接变化。
参考回答
按延迟、带宽、丢包、抖动、断连和切换定义条件,再考虑建连、上传、响应返回等具体阶段及方向。 单纯增加固定延迟不足以覆盖这些行为。
检查超时、取消、提示、资源释放与恢复,同时核对服务端最终状态和是否重复提交。确认工具实际作用于目标流量,并保留注入与撤除证据。
7.4 请求超时后重试成功,为什么还要核对服务端数据?
考察重点:未知结果、重复副作用与幂等验证。
相关内容:提交后的超时与重试。
参考回答
超时可能发生在服务端提交之后,第一次操作已经产生副作用,重试成功仍可能造成第二次效果。 因此需要按业务标识核对记录、金额或状态变化。
覆盖提交前失败、提交后响应丢失、重复并发与相同标识不同参数。既检查响应,也检查最终效果与重试次数,不能只证明第二次返回成功。
7.5 故障工具显示实验结束,怎样确认系统确实恢复?
考察重点:撤销注入、资源恢复与最终状态。
相关内容:恢复与回归。
参考回答
先确认注入已经从实际路径撤除,再检查新业务请求、延迟、资源和积压,并核对故障期间任务的最终数据。 工具结束不代表连接池、队列和业务状态都恢复。
对未完成、重复、丢失和补偿失败分别记录证据及后续处理。实验结果应区分注入、故障期间和恢复三个阶段。
8. 参考资料
- Shopify:Toxiproxy
- Chaos Mesh:Define the Scope of Chaos Experiments
- Google SRE:Testing for Reliability
- RFC 9110:Idempotent Methods
来源:Coding 101 原文 · 免费阅读,引用请保留出处。内容使用说明