LabHub
学习 学习路径 课程

ICA — Istio 认证助理

响应失败了,但业务已经提交

在 LabHub 中继续学习

目标

比较真实 Istio 重试与业务记录,实现事务边界:即使出现响应超时或进程终止,也能防止同一业务重复执行。

为什么重要

200 并不能证明业务只执行了一次,504 也不能证明已经回滚。如果同一订单的每次传输尝试都生成新的业务键,防重机制也会失效。反过来,把内容相同的不同订单合并,同样是错误。本实验会同时处理真实服务网格中的合成 HTTP 业务,以及独立事务代码修改。学生代码修改会应用于临时 SQLite 测试,不会自动替换 HTTP 服务器代码。

步骤

  1. 把 python3 /opt/fixtures/ica-retry/runtime.py inventory 的全部输出保存到 /root/ica-retry/inventory.json。namespace 为 ica-retry,并且 client 与 upstream 的真实代理必须就绪。
  2. 在 /root/ica-retry/routes.yaml 中创建 networking.istio.io/v1 VirtualService retry-lab。namespace 为 ica-retry;hosts 与所有目标均为 upstream.ica-retry.svc.cluster.local;端口为 8080。第一条路由 flaky 对 /flaky 和 /naive 两个 exact match 生效,timeout 为 1s,attempts 为 2,perTryTimeout 为 1s,retryOn 为 '503',retryIgnorePreviousHosts 为 false。第二条路由 lost 对 /lost exact 生效,timeout 为 500ms,attempts 为 0。最后一条 control 不设置 match,指向相同目标,attempts 为 0。应用后执行 collect retry,比较 retry.json 中真实的接收次数与业务次数。
  3. 对同一工具执行 collect timeout。timeout.json 必须同时包含 /lost 的客户端 504、服务器已提交的响应,以及使用同一键调用 /charge 后的恢复结果。不要直接编造数字写入文件。
  4. 通过 collect conflict 保存 conflict.json。确认使用同一业务键、把金额从 100 改为 200 的请求会收到 409,而现有业务金额仍保持为 100。
  5. 通过 collect durability 保存 durability.json。工具只会真正删除并重新创建这台 VM 内的 upstream Pod。client UID 应保持相同,upstream UID 与服务器 instance 必须发生变化;同一业务键和响应 ID 则必须保留。
  6. 把 /opt/fixtures/ica-retry/broken.py 复制为 /root/ica-retry/transaction.py,修复 charge 默认执行路径中的分离提交缺陷。保留 initialize、charge、Conflict,以及 after-business、after-key、after-commit 强制终止点。提交前,业务与键的数量都应为 0;提交后,两者都应为 1;重试后,业务数量始终必须为 1。
  7. 使用 transaction_probe.py /root/ica-retry/transaction.py identity 验证 8 个使用相同键的并发请求。新处理只能有一个;同一键的客户或金额变化必须产生 Conflict;JSON 顺序变化应得到相同响应;不同键但内容相同的请求应作为独立业务。无效金额和未知字段应通过 ValueError 拒绝。
  8. 在 /root/ica-retry/incident.json 中记录:额外尝试 2、最大尝试 3、naive 业务 3、防重业务 1。timeout_means_rollback 与 external_payment_exactly_once 为 false。changed_payload_status 填写真实冲突代码,durable_scope 填写 same-vm-disk,atomic_scope 填写 single-sqlite-file。保留此前所有真实观测,用于最终评分。

参考

记录真实代理与 Pod 身份

使用 inventory 命令记录两个 Pod 的 UID。不要只看 Running 名称,还要确认真实代理已经就绪。

区分三次接收与三次业务执行

应用 routes.yaml 并执行 collect retry。attempts 表示额外尝试次数,应分别查看 /naive 与 /flaky 的业务账本。

在 504 后恢复已经提交的业务

确认 /lost 的预算为 500ms 且不重试。collect timeout 会使用同一业务键再次查询 /charge。

拒绝同一键对应的不同金额

collect conflict 的两个请求使用相同业务键,只有金额不同。确认现有业务与响应是否得到保留。

更换 Pod 后仍返回同一业务响应

collect durability 只会替换这台 VM 中的 upstream。Pod UID 与服务器 instance 应改变,业务 ID 则必须相同。

通过代码修复分离提交缺陷

把 broken.py 复制为 transaction.py 并阅读代码。查找 charge 默认执行路径中,业务写入与防重记录之间是否夹着一次提交。

并发请求与独立业务的边界

使用相同键的并发请求应合并为一个业务,不同键但内容相同的请求则应保留为独立业务。同时确认内容变化会被拒绝,并检查 JSON 顺序变化。

区分响应与业务的事故报告

区分最大尝试次数与真实业务效果,并把保证范围限制为单个 SQLite 文件与同一 VM 磁盘。