背景
业务上线前,常规压测方法(JMeter、ab、wrk)都模拟不出来真实流量的"乱"——真实的用户行为、参数分布、慢请求、错误重试,真实流量里才有。
tcpcopy 是国产开源的流量复制工具,能把生产环境的真实 TCP 流量原样复制到测试机,完全真实地回放。
适用场景:
- 新版本上线前的真实流量压测
- 性能调优(数据库加索引、改 SQL、缓存优化)的量化对比
- 故障复现——线上出了 bug,把流量引到测试机反复重现
- 容量规划——评估"双 11 流量峰值需要多少机器"
前置知识:
- TCP/IP 协议基础
- Linux 内核路由
- libpcap / iptables 基础
重要原则:tcpcopy 会修改源 IP 让响应走辅助机,生产环境用要谨慎:
- 做好"白名单 + 流量比例"控制,先
n 1(1 倍流量)再 n 2(2 倍) - 测试机配置必须等同生产(CPU、内存、网卡、数据库)
- 千万不要把响应数据写回生产库——确认测试库/影子表
一、tcpcopy 工作原理
tcpcopy 不是简单"复制 TCP 包",而是完整模拟 TCP 三次握手 + 数据交互:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| tcpcopy 进程 intercept 进程
(线上机器) (辅助机器)
│ │
[生产客户端] ──TCP──> [线上服务器] │
│ ↑ │
│ │ 1. 抓 IP 层包到 tcpcopy │
│ │ │
│ │ 2. 改目的 IP → 测试机 │
│ └──────────────────────> [测试机] │
│ 3. 改源 IP → 辅助机 IP │
│ 4. 测试机处理请求,返回响应 │
│ 5. 响应到辅助机,被 intercept 截获 │
│ 6. drop 响应 body,复制 IP header ─>│
│ 7. 发送响应头给 tcpcopy │
│ 8. tcpcopy 模拟响应回客户端 │
↓ ↓
[生产客户端] <──TCP── [线上服务器] ← 响应用辅助机的 IP 头 ──
|
三台机器(经典架构):
| 角色 | 数量 | 作用 |
|---|
| 线上服务器 | 1 | 安装 tcpcopy,抓生产包并转发 |
| 测试机 | 1 | 安装实际服务,接收复制流量 |
| 辅助机 | 1 | 安装 intercept,截获测试机响应、回传响应头 |
两台机器(简化):辅助机和测试机合一,前提是辅助机有 IP 层抓包能力(同机房内通常可以)。
二、安装
2.1 tcpcopy(线上机器)
1
2
3
4
5
6
7
8
9
10
11
| # 源码
wget https://github.com/session-replay-tools/tcpcopy/archive/refs/tags/v1.2.0.tar.gz
tar xvf v1.2.0.tar.gz
cd tcpcopy-1.2.0
./configure --prefix=/opt/tcpcopy/
make
make install
# 打包(方便多机部署)
cd /opt
tar -zcvf tcpcopy.tar.gz tcpcopy
|
2.2 intercept(辅助机器)
1
2
3
4
5
6
7
8
9
10
| # 依赖
yum -y install libpcap-devel
# Ubuntu: apt install libpcap-dev
wget https://github.com/session-replay-tools/intercept/archive/refs/tags/1.0.0.tar.gz
tar xvf 1.0.0.tar.gz
cd intercept-1.0.0
./configure --prefix=/opt/tcpcopy/
make
make install
|
三、IP 与端口规划
假设:
- 线上服务器 IP:
9.9.8.7(公网/业务) - 客户端网段:
62.135.200.0/24(生产用户 IP 段) - 测试机 IP:
10.1.2.3(内网,性能等同生产) - 辅助机 IP:
10.1.2.4(与测试机同网段,能抓到测试机回包)
线上服务器服务端口:8881(业务端口)
四、配置步骤
4.1 辅助机启动 intercept
1
2
3
4
| # 默认端口 36524
/opt/tcpcopy/sbin/intercept -i any \
-F 'tcp and (port 8881 or port 8882)' \
-p 40003 -d
|
参数:
| 参数 | 含义 |
|---|
-i any | 监听所有网卡 |
-F 'tcp and src port 8881' | BPF 过滤器:抓 TCP 源端口 8881 的包 |
-p 40003 | intercept 与 tcpcopy 通信端口 |
-d | daemon 模式 |
4.2 测试机配置路由(把响应导向辅助机)
1
2
3
| # 在测试机(10.1.2.3)上执行
# 把客户端网段 62.135.200.0/24 的响应路由到辅助机 10.1.2.4
route add -net 62.135.200.0 netmask 255.255.255.0 gw 10.1.2.4
|
4.3 线上服务器启动 tcpcopy
1
2
3
4
5
| /opt/tcpcopy/sbin/tcpcopy \
-x 8881-10.1.2.3:8881 \
-s 10.1.2.4:40003 \
-c 62.135.200.0/24 \
-d
|
参数:
| 参数 | 含义 |
|---|
-x 8881-10.1.2.3:8881 | 把 8881 端口流量复制到测试机 8881 |
-s 10.1.2.4:40003 | intercept 通信地址 |
-c 62.135.200.0/24 | 客户端 IP 段(必须,否则不复制) |
-d | daemon 模式 |
五、流量控制实战
5.1 复制指定比例流量
1
2
3
4
5
6
7
| # 复制 20% 流量
/opt/tcpcopy/sbin/tcpcopy -x 18001-10.1.2.3:18001 \
-s 10.1.2.4 -c 62.135.200.0/24 -r 20 -d
# 复制 2 倍流量(流量放大,做压测峰值用)
/opt/tcpcopy/sbin/tcpcopy -x 18001-10.1.2.3:18001 \
-s 10.1.2.4 -c 62.135.200.0/24 -n 2 -d
|
5.2 多端口同时复制
1
2
3
4
| /opt/tcpcopy/sbin/tcpcopy \
-x 80-61.135.233.160:8080 \
-x 81-61.135.233.160:8081 \
-s 61.135.233.161 -c 62.135.200.0/24 -d
|
5.3 复制 HTTPS
tcpcopy 抓的是IP 层包,对应用层透明,HTTPS/MySQL/Redis 都能复制。
但 HTTPS 复制后,测试机的 SSL 证书需要能"通过校验"——可以用 -k(客户端跳过证书校验)或导入相同证书。
六、完整生产级命令
线上服务器(iptables 配合,把响应包标记后让 tcpcopy 处理):
1
2
3
4
5
6
7
8
9
| # 让 tcpcopy 处理的端口
iptables -I INPUT -p tcp --sport 40001 -j DROP -s <测试机IP>
iptables -I INPUT -p tcp --sport 8881 -j DROP -s <测试机IP>
iptables -I OUTPUT -p tcp --sport 40001 -j NFQUEUE
/opt/tcpcopy/sbin/tcpcopy \
-x 8787-<测试机IP>:40001 \
-s <辅助机IP>:40003 \
-c <客户端网段> -d
|
辅助机:
1
2
3
4
| /opt/tcpcopy/sbin/intercept \
-i em1 \
-F 'tcp and src port 8881' \
-p 40003 -d
|
测试机(路由 + 服务启动):
1
2
| route add -net <客户端网段> netmask 255.255.255.0 gw <辅助机IP>
# 启动业务服务,监听 8881
|
七、GLIBC_2.34 兼容性坑
最常见的错误:
1
2
| /opt/tcpcopy/sbin/tcpcopy: /lib/x86_64-linux-gnu/libc.so.6:
version `GLIBC_2.34' not found (required by /opt/tcpcopy/sbin/tcpcopy)
|
原因:tcpcopy 1.2.0 是 2022 年编译的,链接到 GLIBC 2.34(Ubuntu 22.04 默认)。如果线上服务器是 Ubuntu 20.04(GLIBC 2.31),就会报错。
对策:
方案 A:升级 OS 到 Ubuntu 22.04+(推荐)
方案 B:在 Ubuntu 22.04 编译后部署
1
2
3
4
5
| # 拿 22.04 容器编译
docker run --rm -v $(pwd):/src ubuntu:22.04 bash -c "
apt update && apt install -y build-essential libpcap-dev
cd /src && ./configure --prefix=/opt/tcpcopy/ && make && make install
"
|
方案 C:打补丁降级
在 configure.ac 中改 -D_GNU_SOURCE 处理,不推荐——容易出诡异 bug。
查看 glibc 版本:
1
2
| strings /lib/x86_64-linux-gnu/libc.so.6 | grep GLIBC_
# 应看到 GLIBC_2.31 / GLIBC_2.34 等
|
八、常见问题
8.1 “many connections can’t be established”
tcpcopy 复制时建立大量连接,可能与以下冲突:
- 测试机 ulimit 限制
- 测试机 TIME_WAIT 累积
- 辅助机抓包丢包(libpcap buffer 太小)
对策:
1
2
3
4
5
6
7
| # 测试机优化
sysctl -w net.ipv4.tcp_tw_reuse=1
sysctl -w net.ipv4.ip_local_port_range="1024 65000"
ulimit -n 65535
# intercept 增大 buffer
/opt/tcpcopy/sbin/intercept -i any -F ... -p 40003 -d -m 4096
|
8.2 测试机收到的 IP 全是辅助机 IP
正常——这是 tcpcopy 故意改的(让响应回辅助机)。如果你的服务强依赖客户端 IP(如 IP 白名单、IP 限流),需要用其他工具(nginx X-Forwarded-For)。
8.3 数据库连接爆掉
复制 N 倍流量意味着测试数据库要承受 N 倍写入。对策:
- 测试库用
binlog_format=ROW + read_only 防止误写 - 用影子表(按用户分表)
- 用 ShardingSphere 影子库
8.4 tcpcopy 进程秒退
查看日志:
1
2
| /opt/tcpcopy/sbin/tcpcopy -x ... -d 2>&1 | tee tcpcopy.log
# 去掉 -d 改前台
|
常见:libpcap 权限不够(用 root 跑)、intercept 端口不通(防火墙)。
九、卸载
1
2
3
4
| rm -rf /opt/tcpcopy
rm -rf /opt/tcpcopy.tar.gz
# 清理路由
route del -net 62.135.200.0 netmask 255.255.255.0 gw 10.1.2.4
|
小结
tcpcopy 是真实流量回放的事实标准:
- 三机架构:线上 + 测试机 + 辅助机
- 流量控制:
-r 比例复制、-n 流量放大 - 应用层透明:HTTP/HTTPS/MySQL/Redis 都能复制
- GLIBC 坑:在 Ubuntu 22.04+ 编译部署
生产使用三原则:
- 先 1 倍再放大,避免误伤
- 测试库用 read_only + 影子表
- 看响应指标而非客户端数(测试机 CPU、DB QPS、慢 SQL)
下一步:
- 商业方案:GorillaStreamer、NetStitcher
- 应用层工具:GoReplay(HTTP 专用,更简单)
- 影子库:ShardingSphere 影子规则
2024 视角:流量复制进入 eBPF + 服务网格时代
2022 那篇是 tcpcopy 1.2.0 时代的"IP 层抓包复制"。2024 视角下,流量复制有了更现代的实现——尤其在 K8s/微服务生态。
一、tcpcopy 1.2.0 仍是 2024 的"经典选择"
- 2024 现状:tcpcopy 仓库 session-replay-tools/tcpcopy 维护节奏放缓(2018 后基本没大更新),但功能完整、稳定性高——仍被大量企业用。
- 替代选择:
- goreplay(Go 写,HTTP 专用)
- tcprewrite + tcpreplay(TCP 层回放)
- bilibili/overlord(B 站开源,Go 写,2023+ 活跃)
- eBPF-based:基于 eBPF 的现代方案
二、eBPF 流量复制的"现代方案"
- 2024 主流:用 eBPF 在内核态做流量复制——比 tcpcopy 更灵活。
1
2
3
4
5
6
| # bpftool / tc 的 mirror 动作
tc qdisc add dev eth0 handle ffff: ingress
tc filter add dev eth0 parent ffff: protocol ip u32 match u32 0 0 \
action mirred egress mirror dev eth1
# 镜像 eth0 流量到 eth1(旁路采集)
|
- 优势:
- 内核态抓包,性能零损耗
- 不会改源 IP(vs tcpcopy 改 IP)
- 可镜像到任何网卡 / netns
- 配合 bpf_dump(bpftool)能直接拿到包内容
三、K8s 时代的流量复制:服务网格
- Istio(2024 仍是 K8s 服务网格事实标准)的
VirtualService 镜像功能:
1
2
3
4
5
6
7
8
9
10
11
12
13
| apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: reviews-mirror
spec:
hosts:
- reviews
http:
- mirror:
host: reviews-test # 镜像到测试服务
route:
- destination:
host: reviews # 主流量仍走主服务
|
- 效果:
- 生产流量 100% 走
reviews(主服务) - 同时 100% 流量复制到
reviews-test(测试服务) - 测试服务的响应丢弃(不影响生产)
四、GoReplay 在 HTTP 场景的"现代姿势"
1
2
3
4
5
6
| # 在生产机器
goreplay --input-raw :8080 --output-http "http://test-server:8080"
# 复制 10% 流量
goreplay --input-raw :8080 --output-http "http://test-server:8080" --middleware "/path/to/middleware.go"
# middleware.go: 用 probability 控制复制比例
|
- GoReplay 优势(比 tcpcopy 简单):
- 纯 Go 单文件,不依赖 libpcap
- 不需要路由配置
- 不需要辅助机
- HTTP / WebSocket / HTTPS 都能复制
五、AI 训练数据的"流量录制"
- 2024 LLM / AI 应用最大新需求:生产 LLM API 请求录制做离线评测 / 模型微调:
- LangSmith(LangChain 自带)
- Langfuse(开源,2023 推出,2024 主流)
- Helicone(LLM 专用 observability)
1
2
3
4
5
6
7
| # Langfuse 录 LLM 请求
pip install langfuse
from langfuse.decorators import observe, langfuse_context
@observe()
def llm_call(prompt: str) -> str:
return openai.ChatCompletion.create(model="gpt-4", messages=[{"role": "user", "content": prompt}])
|
- 价值:把生产真实 prompt / response 录下来,做 Few-shot 训练数据。
六、数据库流量复制的"现代姿势"
2017 那篇没提,2024 数据库压测有专属方案:
- MySQL Traffic Replay(MySQL 8.0+ 内置
mysqlbinlog | mysql 工具) - PostgreSQL pg_replay(第三方)
- ShardingSphere 影子库:应用层流量分流
- tcpcopy + 数据库代理(如 ProxySQL):用 tcpcopy 抓 MySQL 流量复制到测试库
1
2
3
4
5
6
7
| -- MySQL 8.0 录 binlog
mysqlbinlog --read-from-remote-server --host=prod-db --user=repl --password=xxx \
--raw --stop-never-slave-server-id=1 \
mysql-bin.000001 > /tmp/binlog.sql
# 拿到测试库
mysql -h test-db < /tmp/binlog.sql
|
七、流量复制的"合规边界"
- 生产流量包含用户数据(手机号、地址、身份证)—— 复制到测试环境直接违反《数据安全法》《个保法》。
- 2024 合规要点:
- 脱敏:复制前 mask 敏感字段
- 审计:所有流量复制要留审计日志
- 数据隔离:测试环境的流量复制数据不能进生产库
- 用途限制:复制的流量只能用于压测、调试,不能用于训练、对外展示
1
2
3
4
5
6
7
8
9
| # 流量脱敏示例
import re
def mask_pii(payload: dict) -> dict:
if 'phone' in payload:
payload['phone'] = re.sub(r'(\d{3})\d{4}(\d{4})', r'\1****\2', payload['phone'])
if 'id_card' in payload:
payload['id_card'] = payload['id_card'][:6] + '********' + payload['id_card'][-4:]
return payload
|
源文档:os/linux/第三方tools/net/tcpcopy/tcpcopy.md(完整安装步骤、路由配置、GLIBC 报错、iptables 配合)