网络层面的混沌工程
以下内容来自与Gemini的对话后,Gemini的回答
你想要做的这个Monkey测试(混沌工程)——即模拟系统互联抖动(Jitter)、丢包、异常,来验证数据完整性(Data Consistency/Integrity),这正是分布式系统中最核心、最难测试的部分。
虽然 bpftrace 做这个比较吃力,但 Linux 内核自带了一套神器,专门用来做这件事。它不需要你写代码,只需要敲几行命令,就能精准模拟各种“烂网络”。
这个神器叫做 tc (Traffic Control) 配合 netem (Network Emulator)。
我来教你如何用它们构建一个**“数据完整性地狱”**,来看看你的后端代码能不能扛得住。
—
核心武器:TC + Netem
tc 是 Linux 内核网络栈自带的流量控制器。它可以工作在网卡(Network Interface)的出口,对数据包进行排队、整形、延迟、丢弃。
不需要安装任何软件,你的 Rocky Linux 里就有。
场景一:模拟“网络抖动” (Network Jitter)
测试目标: 验证后端是否有超时重试机制?重试会不会导致数据重复插入(幂等性问题)?
命令:
Bash
# 在 eth0 网卡上,添加 100ms 的延迟,并带有 +/- 20ms 的随机抖动
# 且遵循正态分布 (distribution normal)
sudo tc qdisc add dev eth0 root netem delay 100ms 20ms distribution normal
效果:
- 一个 ping 包过去,可能是 80ms,也可能是 120ms。
- 数据完整性风险: 客户端请求发出了,后端收到了并处理了,但在返回 ACK 时网络抖动导致超时。客户端以为失败了,又发了一次。结果:数据库里存了两条一样的订单。
场景二:模拟“随机丢包” (Packet Loss)
测试目标: 验证 TCP 断连后的事务回滚(Transaction Rollback)是否正常?
命令:
Bash
# 随机丢弃 10% 的数据包
sudo tc qdisc add dev eth0 root netem loss 10%
效果:
- 你的 SSH 可能会变得非常卡。
- 数据完整性风险: 在一个涉及多步操作的事务中(比如:扣款 -> 加积分),扣款请求到了,加积分请求丢包了。结果:钱扣了,积分没加(数据不一致)。
场景三:模拟“包损坏”与“乱序” (Corruption & Reordering)
测试目标: 验证应用层协议的校验能力。
命令:
Bash
# 随机让 5% 的包发生比特翻转(损坏),让 25% 的包不按顺序到达
sudo tc qdisc add dev eth0 root netem corrupt 5% reorder 25%
—
进阶:如何只搞“数据库”,不搞“SSH”?
直接在 eth0 上搞 netem 会影响所有流量,把你自己的 SSH 也就搞卡了。我们需要精准打击。
这就需要用到 tc filter 或者更简单的 IFB (Intermediate Functional Block) 设备,但为了简单,我推荐一个更好用的封装工具:comcast 或者 pumba(如果你用 Docker)。
但如果你想手动用原生命令,可以配合 cgroup 或者端口过滤。这里有一个简单的端口级故障注入流程:
目标:只干扰连接 MySQL (3306) 的流量,模拟写入延迟。
Bash
# 1. 创建一个优先队列 (PRIO)
sudo tc qdisc add dev eth0 root handle 1: prio
# 2. 在第 1 个子队列上应用 500ms 延迟 (这就是我们要的故障)
sudo tc qdisc add dev eth0 parent 1:1 handle 10: netem delay 500ms
# 3. 设置过滤器:凡是发往 3306 端口的包,扔到第 1 个子队列(故障队列)
sudo tc filter add dev eth0 protocol ip parent 1:0 prio 1 u32 match ip dport 3306 0xffff flowid 1:1
# 4. 其他流量默认走第 2/3 队列 (不受影响)
—
架构师视角:如何设计“数据完整性”验证实验?
有了上面的工具,你就可以设计实验了。单纯的“卡”没有意义,你需要观测数据。
实验设计模板:
- 基准状态 (Baseline):
- 后端是一个简单的转账接口:A -> B 转 100 元。
- 编写一个脚本,并发跑 1000 次转账。
- 预期: A 减了 100000,B 加了 100000。
- 注入故障 (Chaos Injection):
- 使用 tc 命令,设置 30% 丢包 + 200ms 延迟。
- 开启故障。
- 运行负载 (Run Workload):
- 再次运行那 1000 次转账脚本。
- 你会看到客户端报很多 Timeout Exception 或 Connection Reset。
- 验证一致性 (Verify Consistency):
- 这是最关键的一步。
- 等待所有请求结束(包括重试)。
- 查询数据库:
- 情况 A (完美): A 减去的金额 == B 增加的金额。
- 情况 B (数据丢失): A 减了钱,但 B 没收到(丢包导致事务提交了一半?)。
- 情况 C (数据重复): B 收到的钱比 A 减去的还多(超时重试导致重复入账)。
推荐工具:Shopify 的 Toxiproxy
如果你觉得 Linux 的 tc 命令太晦涩,太容易把机器搞断网。我强烈推荐你使用 Shopify 开源的 Toxiproxy。
- 它是应用层的: 不需要 root 权限,不需要改内核。
- 原理: 它在你的 Client 和 Database 之间架一个代理(Proxy)。
- 玩法:
- 你连接 Toxiproxy 的端口,Toxiproxy 连数据库。
- 通过 HTTP API 告诉 Toxiproxy:“嘿,把现在的连接切断 1 秒钟”或者“把现在的延迟加到 5 秒”。
- 它非常适合自动化测试代码集成。
总结:
要测试系统互联抖动对数据的影响:
- 底层派: 用 Linux 原生 tc + netem。真实、硬核、能模拟 TCP 协议栈级别的故障。
- 应用派: 用 Toxiproxy。简单、安全、易于集成到 CI/CD 流水线。
这才是真正的“后端内功”修炼。如果你的系统能在 tc qdisc add … loss 30% 的情况下依然保持账目平齐,那就是真正的高可用系统。