超时、重试与幂等实战
验证:未记录
- 适用版本
- 未记录
- 验证范围
- 尚未登记验证记录。
客户端超时只能证明它没等到结果,不能证明服务端没执行。 HTTP 是 Hypertext Transfer Protocol(超文本传输协议)。
1. 不同超时不是同一个时钟#
| 类型 | 控制什么 | 常见误判 |
|---|---|---|
| 连接超时 | 解析/连接/握手等建立阶段,具体范围看客户端 | 不能替代整个请求截止时间 |
| 读取/空闲超时 | 两次读操作或数据活动之间的等待 | 持续少量数据可能让总时长无限延长 |
| 总截止时间 deadline | 从发起到结束的整体预算 | 应包含排队、所有尝试与退避 |
| 连接池等待超时 | 等可用连接 | 服务端不慢,客户端也可能排队很久 |
短查询可从“依赖 2 秒、应用总预算 4 秒、代理等待 5 秒、客户端总预算 6 秒”开始评估;这是示例预算,不是所有接口统一默认值。应用要传播剩余预算和取消信号,不能每一跳重新获得完整 6 秒。
ALB(Application Load Balancer,应用负载均衡器)的 idle timeout 是空闲间隔,不是业务总 deadline;后端连接的空闲关闭时间通常应长于 ALB 的对应时间,避免复用已关闭连接。不要与“依赖操作超时应较短”混淆。ALB 超时说明。
2. 重试策略#
- 先判断操作能否安全重复,再判断错误是否暂时性。参数错误、授权失败一般不重试;429/503 按服务契约和 Retry-After 处理。
- 使用有限次数、指数退避和随机抖动;若 Retry-After 超出剩余预算,停止而不是立刻重试。
- 一处负责重试,避免客户端、网关、应用每层三次造成请求放大。重试流量也计入容量预算。
- 幂等键按租户与业务操作隔离;同键同内容复用结果,同键不同内容拒绝;原子落库,并覆盖并发及保留期限。
3. Hands-on A:请求超时但并非服务端报错#
先启动 专题隔离环境,其中 slow.bin 为 64 KiB、限速约 1 KiB/s:
curl -sS --max-time 2 -o /dev/null -w 'http=%{http_code} bytes=%{size_download} total=%{time_total}\n' http://127.0.0.1:18080/slow.bin
# 预期 curl 退出码 28;可能已经收到 HTTP 200,但正文没下载完。
curl -sS --max-time 5 -o /dev/null -w '%{http_code}\n' http://127.0.0.1:18080/health在无 set -e 的交互终端运行预期失败的命令;不要把“HTTP 200”作为下载完整性的唯一判据。
4. Hands-on B:用真实订单工程验证重复提交#
先完成 订单参考工程 的部署与测试租户配置。准备 HTTPS 入口、已有测试租户 token、jq、Python 3;下面使用该工程真实 /orders 契约,不适用于任意订单系统。
export ORDERS_BASE='https://orders.staging.example.com'
export TEST_TENANT='替换为token对应的测试租户'
umask 077
HTTP_WORK=$(mktemp -d)
# Bash 下静默读入,避免 token 落入命令历史和进程参数。
read -r -s -p 'Test bearer token: ' TEST_TOKEN; printf '\n'
printf 'Authorization: Bearer %s\n' "$TEST_TOKEN" > "$HTTP_WORK/auth.header"
unset TEST_TOKEN
IDEM=$(python3 -c 'import uuid; print(uuid.uuid4())')
jq -n --arg tenant "$TEST_TENANT" '{tenant:$tenant,amount_cents:1200,synthetic:true}' > "$HTTP_WORK/order.json"
for attempt in 1 2; do
curl -sS --connect-timeout 3 --max-time 10 -H "@$HTTP_WORK/auth.header" -H "Idempotency-Key: $IDEM" -H 'Content-Type: application/json' --data-binary "@$HTTP_WORK/order.json" -o "$HTTP_WORK/order-$attempt.json" -w '%{http_code}\n' "$ORDERS_BASE/orders"
done
jq -s -e '.[0].id != null and .[0].id == .[1].id' "$HTTP_WORK"/order-[12].json
jq '.amount_cents=1201' "$HTTP_WORK/order.json" > "$HTTP_WORK/conflict.json"
curl -sS --max-time 10 -H "@$HTTP_WORK/auth.header" -H "Idempotency-Key: $IDEM" -H 'Content-Type: application/json' --data-binary "@$HTTP_WORK/conflict.json" -o "$HTTP_WORK/conflict-response.json" -w '%{http_code}\n' "$ORDERS_BASE/orders"预期首次 201、重复 200 且 ID 相同、不同内容 409。再用测试工程的验收程序检查账本/投影只产生一次业务效果;仅响应 ID 一样不够。进一步的并发和进程崩溃测试使用参考工程自带测试,不在生产强杀进程。
保存合成订单 ID,按测试环境留存/清理规则处理数据;删除本次凭证文件:rm -r -- "$HTTP_WORK"。不提供全库删除命令。curl 的时间预算选项见 官方文档。