TCPCopy 是一个 TCP 流回放工具,用于对互联网服务器应用进行逼真的测试。
尽管真实的在线流量对于测试互联网服务器应用至关重要,但由于在线环境的复杂性,精确模拟它是一项挑战。为了实现更逼真的测试,TCPCopy 被开发为一种实时流量再现工具,能够生成与生产工作负载高度相似的测试工作负载。TCPCopy 在中国公司中被广泛使用。
TCPCopy 对生产系统的影响极小,仅消耗额外的 CPU、内存和带宽。再现的工作负载在请求多样性、网络延迟和资源使用方面与生产环境一致。

图 1. TCPCopy 架构概览
如图 1 所示,TCPCopy 由两个组件组成:tcpcopy 和 intercept。tcpcopy 组件运行在线上服务器上,负责捕获实时请求;而 intercept 运行在辅助服务器上,负责执行将响应信息传递给 tcpcopy 等任务。测试应用程序本身运行在目标服务器上。
默认情况下,tcpcopy 使用原始套接字在网络层捕获数据包(图中橙色箭头所示)。它负责处理 TCP 交互模拟、网络延迟控制和上层交互模拟等过程,然后使用原始套接字将数据包发送到目标服务器(图中浅红色箭头所示)。
在目标服务器上唯一需要做的任务是配置路由规则,将响应数据包(图中浅绿色箭头所示)导向辅助服务器。
intercept 组件的作用是将响应头部(默认情况下)转发给 tcpcopy。它捕获响应数据包,提取响应头部信息,并通过专用通道(图中浅蓝色箭头所示)将这些信息发送给 tcpcopy。在接收到响应头部后,tcpcopy 利用该信息修改在线数据包的属性,并继续发送后续数据包。
需要注意的是,来自目标服务器的响应被路由到辅助服务器,辅助服务器充当黑洞。
对于 intercept,你有两个选择:
git clone git://github.com/session-replay-tools/intercept.git对于 tcpcopy,你也有两个选择:
git clone git://github.com/session-replay-tools/tcpcopy.gitintercept 目录:cd intercept./configure makeintercept 工具:make installintercept 的配置选项--single
以非分布式模式运行 intercept。
--with-pfring=PATH
指定 PF_RING 库源文件的路径。
--with-debug
启用调试支持编译 intercept,日志保存到文件。
tcpcopytcpcopy 目录:cd tcpcopy./configure maketcpcopy 工具:make installtcpcopy 的配置选项--offline
从 pcap 文件回放 TCP 流。
--pcap-capture
在数据链路层捕获数据包。
--pcap-send
在数据链路层发送数据包,而不是 IP 层。
--with-pfring=PATH
指定 PF_RING 库源文件的路径。
--set-protocol-module=PATH
设置 tcpcopy 与外部协议模块协同工作。
--single
如果 intercept 和 tcpcopy 都配置了 --single 选项,则只有一个 tcpcopy 实例与 intercept 协同工作,从而获得更好的性能。
--with-tcmalloc
使用 tcmalloc 替代 malloc。
--with-debug
启用调试支持编译 tcpcopy,日志保存到文件。
假设 tcpcopy 和 intercept 都使用 ./configure 进行了配置。
在运行服务器应用的目标服务器上:
配置路由规则,将响应数据包导向辅助服务器。例如,如果辅助服务器的 IP 地址是 61.135.233.161,则使用以下路由命令将来自 62.135.200.x 范围客户端的所有响应导向辅助服务器:
route add -net 62.135.200.0 netmask 255.255.255.0 gw 61.135.233.161
在运行 intercept 的辅助服务器上(需要 root 权限或 CAP_NET_RAW 能力):
./intercept -F <filter> -i <device>
注意,过滤器格式与 pcap 过滤器相同。例如:
./intercept -i eth0 -F 'tcp and src port 8080' -d
在这个例子中,intercept 将捕获来自监听 8080 端口的基于 TCP 的应用程序的响应数据包,并使用 eth0 网络设备。
请注意,辅助服务器上未启用 ip_forward。
在线上源服务器上(需要 root 权限或 CAP_NET_RAW 能力):
./tcpcopy -x localServerPort-targetServerIP:targetServerPort -s <intercept server> [-c <ip range>]
例如(假设目标服务器的 IP 地址为 61.135.233.160):
./tcpcopy -x 80-61.135.233.160:8080 -s 61.135.233.161 -c 62.135.200.x
在这个例子中,tcpcopy 从当前服务器的 80 端口捕获数据包,将客户端 IP 地址更改为 62.135.200.x 范围内的地址,并将这些数据包发送到目标服务器(61.135.233.160)的 8080 端口。它还连接到 61.135.233.161,请求 intercept 转发响应数据包。虽然 -c 参数是可选的,但此处使用它来简化路由规则。
CAP_NET_RAW 能力(例如,setcap CAP_NET_RAW=ep tcpcopy)。intercept 的 ./configure --with-resp-payload 选项不能与 tcpcopy 的 ./configure 选项同时使用。ip_forward。./tcpcopy -h 或 ./intercept -h。以下几个因素可能影响 TCPCopy,详情如下。
默认情况下,tcpcopy 在线上服务器上使用原始套接字输入接口在网络层捕获数据包。在高负载下,系统内核可能会丢弃一些数据包。
如果配置了 --pcap-capture,tcpcopy 会在数据链路层捕获数据包,并可以在内核中过滤数据包。使用带有 pcap 捕获的 PF_RING 可以减少丢包。
为了获得最佳捕获效果,可以考虑通过交换机镜像入站数据包,并通过负载均衡器将流量分布到多台机器上。
tcpcopy 默认使用原始套接字输出接口在网络层向目标服务器发送数据包。为避免 ip_conntrack 问题或提高性能,可以使用 --pcap-send 在数据链路层发送数据包。
tcpcopy 发送的数据包在到达目标服务器之前可能面临挑战。如果源 IP 地址是最终用户的 IP(默认情况下),安全设备可能会认为数据包无效或伪造并丢弃。要测试这一点,可以在目标服务器上使用 tcpdump。如果数据包在同一网段内成功发送,但跨网段失败,则数据包可能在途中被丢弃。
为了解决这个问题,可以将 tcpcopy、目标应用和 intercept 部署在同一网段内。或者,在同一网段内使用代理将数据包转发到另一网段的目标服务器。
将目标服务器的应用部署在同一网段内的虚拟机上可能仍然会遇到这些问题。
目标服务器可能使用 rpfilter 来验证源 IP 地址的合法性,从而丢弃被视为伪造的数据包。如果 tcpdump 捕获到了数据包但应用未处理,请检查 rpfilter 设置,并根据需要调整或删除。其他问题如 iptables 设置也可能影响 tcpcopy。
目标服务器上的应用可能无法及时处理所有请求。应用中的错误或限制可能导致响应延迟或套接字缓冲区中存在未处理的请求。
确保辅助服务器上 ip_forward 设置为 false,以防止其路由数据包,并确保其充当黑洞。
首先,在线上服务器上使用 telnet 连接到测试服务器的端口。这将检查网络路径是否可达。如果连接失败,请先解决此问题,然后进行以下诊断。
假设在 tcpcopy 测试期间,测试服务器上的应用没有收到任何请求。确定初始握手数据包(即 SYN 数据包)是否到达测试服务器。
1.1 仅捕获到 SYN 数据包:
如果在测试服务器上使用 tcpdump 看到复制的 SYN 数据包正在到达,这表明它们已到达测试服务器的数据链路层。如果 netstat 显示应用没有连接,则说明数据包在 IP 层被丢弃。检查是否配置了 rpfilter —— 如果是,请移除该设置,问题通常应该解决。如果没有设置 rpfilter,请确认 iptables 设置中没有冲突,并根据需要调整相关规则。
1.2 SYN 后紧跟 RST 数据包: 如果 SYN 数据包之后立即出现复位(RST)数据包(在同一会话中时间间隔小于 1 秒),则表明存在路由问题或冲突,导致响应数据包直接发送回真实的客户端。
1.3 测试服务器响应第二个握手数据包: 在辅助服务器上捕获数据包,检查第二个握手数据包是否已到达。
如果数据包未到达辅助服务器, 表明路由设置未生效,因此 intercept 无法捕获第二个握手数据包,从而阻止了后续回放。一种可行的解决方案是直接在测试服务器上运行 intercept(注意:保持路由设置不变,并确保 tcpcopy 中的 -c 参数未设置为 tcpcopy 用于连接 intercept 的 IP 地址,否则 tcpcopy 将无法连接到 intercept)。
如果捕获到了第二个握手数据包, 检查是否启用了 ip_forward。如果已启用,请禁用它,因为它可能导致响应数据包直接发送回客户端,干扰测试。
2.1 在线上服务器上捕获到 tcpcopy 数据包:
如果在线上服务器上使用 tcpdump 捕获到了 tcpcopy 转发后的数据包,但数据包未到达测试服务器,则说明它们在中途被丢弃。可以尝试在 tcpcopy 中使用 -c 参数将客户端 IP 地址修改为一个有效的地址。在极端情况下,将客户端 IP 设置为运行 tcpcopy 的机器的 IP 地址(注意:可能会出现 NAT 问题,并且如果 intercept 运行在测试服务器上,请确保 tcpcopy 中的 -c 参数未设置为 tcpcopy 用于连接 intercept 的 IP 地址,否则 tcpcopy 将无法连接到 intercept)。
2.2 在线上服务器上未捕获到 tcpcopy 数据包:
如果在 tcpcopy 的日志中没有看到 all clt:xx 信息, 表明 tcpcopy 无法在 IP 层捕获数据包。在这种情况下,使用 --pcap-capture 选项在数据链路层捕获数据包。设置 -F 参数(例如 'tcp and dst port 80 and dst host 10.100.1.2')和 -i 参数(网络接口),以绕过 IP 层捕获。
如果在 tcpcopy 的日志中看到 all clt:xx,其中 xx > 0, 说明 tcpcopy 成功捕获了数据包,但该数据包在线上服务器的 IP 层被过滤掉了。检查 iptables 对输出链的限制等其他设置。如果 iptables 是问题所在且无法在线上服务器上修改,请使用 --pcap-send 选项从数据链路层发送数据包。
发现 Bug 或有功能请求?请提交新的 Issue。在提交任何 Issue 之前,请先搜索现有 Issue。
版权所有 2025,基于 BSD 许可。
多位人士在本文档的编写过程中审阅了草稿并提供了反馈。我特别感谢王鸿深的贡献。