流量复制导流工具研究

背景

Web 应用压力测试工具有很多,比如Apache ab, Apache JMeter等。但是这些工具都没能解决一个问题:如何正确模拟生产环境的流量。对于线下的传统压力测试,难以模拟真实流量,尤其难以模拟正常流量混杂着各色异常流量。

系统重构或重要变更上线前,可以复制线上真实流量,实时模拟线上流量,甚至可以放大真实流量,进行压力测试,以评估系统承载能力。反过来也可以这样,如果线上跑着发现有性能瓶颈,但线下环境难以复现,还不如把真实流量拷贝到线下重放,毕竟线下环境便于上各种排查手段,重放几遍都行,直到找到问题。

因此,需要研究如何复制线上真实流量并引流到测试环境。主要的工具有TCPCopy和GoReplay 。下面分别介绍这两个工具。

TCPCopy

TCPCopy 是一种请求复制(复制基于 TCP 的 packets)工具 ,通过复制在线数据包,修改 TCP/IP 头部信息,发送给测试服务器,达到欺骗测试服务器的TCP 程序的目的,从而为欺骗上层应用打下坚实基础。

TCPCopy由网易技术部的王斌在王波的工作基础上中2010年开发,并与2011年9月开源。Github地址,最新的发布版本号是 1.2.0。其应用领域较广,目前已经应用于国内各大互联网公司,很多公司的模拟在线测试都是基于 TCPCopy 做的。

总体说来,tcpcopy主要有如下功能:
1)分布式压力测试工具,利用在线数据,可以测试系统能够承受的压力大小(远比ab压力测试工具真实地多),也可以提前发现一些bug
2)普通上线测试,可以发现新系统是否稳定,提前发现上线过程中会出现的诸多问题,让开发者有信心上线
3)对比试验,同样请求,针对不同或不同版本程序,可以做性能对比等试验
4)利用多种手段,构造无限在线压力,满足中小网站压力测试要求
5)实战演习(架构师必备)

tcpcopy可以用于实时和离线回放领域,并且tcpcopy支持mysql协议的复制,开源二年以来,功能上越来越完善。

架构如下图所示:
在这里插入图片描述
线上服务器Online Server(OS):上面要部署 TCPCopy,从数据链路层(pcap 接口)抓请求数据包,发包是从IP层发出去;
测试服务器Test Server(TS):最新的架构调整把 intercept 的工作从 TS 中 offload 出来。TS 设置路由信息,把 被测应用 的需要被捕获的响应数据包信息路由到 AS;
辅助服务器Assistant Server(AS):这是一台独立的辅助服务器,原则上一定要用同网段的一台闲置服务器来充当辅助服务器。AS 在数据链路层截获到响应包,从中抽取出有用的信息,再返回给相应的 OS 上的 tcpcopy 进程。

TCPCopy实验

1. 实验环境

线上服务器 --> 192.168.119.158
测试服务器 --> 192.168.119.152
辅助服务器 --> 192.168.119.151

•tcpcopy运行在线上服务器上,tcpcopy会把线上服务器收到的流量,重放给测试服务器,重放的时候tcpcopy修改了IP数据包的源IP地址(譬如修改源地址为192.168.2.254)。所以,线上服务器和测试服务器应该部署相同的服务。
•tcpcopy把源IP伪造成192.168.2.254的包发给了测试服务器,如此,测试服务器在处理完tcpcopy发过来的数据以后,会把这些数据包返回给客户端,即伪造的192.168.2.254。
•由于没有192.168.2.254这个地址,我们在测试服务器添加一条专门的路由,把发往192.168.2.0/24的数据包,都全部转交给辅助服务器。
•为了保证辅助服务器会接受这些本不属于自己的,部署在辅助服务器上的intercept就发挥作用了。辅助服务器还可以用来把客户端的请求返回给tcpcopy,但是默认只返回响应头部给tcpcopy。辅助服务器应该类似于黑洞。

2. 线上机器安装tcpcopy

在线上服务器上下载,然后编译安装tcpcopy的包,如下:

wget https://github.com/session-replay-tools/tcpcopy/archive/v1.2.0.tar.gz
tar xvf tcpcopy-1.2.0 
cd tcpcopy-1.2.0
./configure
make 
make install

3. 辅助机器安装intercept

yum -y install libpcap-devel 
wget https://github.com/session-replay-tools/intercept/archive/1.0.0.tar.gz
tar xvf 1.0.0.tar.gz
cd intercept-1.0.0
./configure
make
make install

4. 部署

简单的在线上服务器和测试服务器上启动一个简单的springboot程序,提供一个http接口,打印并返回hello world。

@RequestMapping("/hello")
private String service()  {
    LOG.info("hello world");
    return "hello world";
}

先在辅助服务器上开启intercept,没有intercept的话,tcpcopy启动不起来:

#/usr/local/intercept/sbin/intercept -i ens33 -F tcp and src port 80 -d

•-i,intercept会监听端口,和tcpcopy进行通信,-i就是指定监听在哪个端口。tcpcopy启动的时候会来连这个端口,如果连不上,就会启动失败。
•-F, 过滤规则,语法和pcap一样。
•-d, 已守护进程方式运行
还有其它参数可以使用,-h便可以查看,不详细解释了。

辅助机的 intercept 通过-F 参数制定捕获规则,捕获tcp连接中源端口为80的所有数据,也即是测试机的回应数据。其作用是将测试机路由过来的响应数据捕获下来,完成整个请求回应的流程且保证测试机产生的回应数据不会流向线上环境。
intercept 会监听 tcp 36524 端口,并和线上服务器的 tcpcopy 之间建立通信,我们通过ss命令可以观察到。

然后,在线上服务器开启tcpcopy:

# /usr/local/tcpcopy/sbin/tcpcopy -x 80-192.168.119.152:80 -s 192.168.119.151 -c 192.168.2.254 -n 2 -d

•-x, 是指本机80端口的流量copy到测试服务器192.168.119.152的80端口
•-s, 指定intercept机器的地址,tcpcopy要和intercept建立连接
•-c 伪装地址,在把流量复制到测试服务器的时候,修改数据包的源地址为192.168.2.254,这样方便指定路由。也可以写成192.168.2.x,这样源地址就是指定网段中的地址了。
•-n 流量放大倍数,如果不是压测目的就不用指定这个参数。
-d 以守护模式运行。

最后,在测试服务器上开启路由(切记辅助服务器要和测试服务器在一个子网里):

# route add -net 192.168.2.0 netmask 255.255.255.0 gw 192.168.119.151

路由的意思是把发往192.168.2.0/24地址的数据包全部转给辅助服务器。这样在辅助机上就可以对全部的回应包进行截获处理了。

5. 测试效果

在另外一台机器上,通过Jmeter向线上服务器连续发起5次请求http://192.168.119.1/hello
在这里插入图片描述

然后查看两个HTTP服务器的实时日志,
线上服务器的结果:
在这里插入图片描述
测试服务器的结果:
在这里插入图片描述

可以看到,正如tcpcopy命令中设置的n=2,在测试服务器上,请求变成了10次,明显看到流量被放大了2倍。效果正如预期。

通过tcpdump抓包查看:

tcpdump -n -i ens33 port 80 -w tcpcopy.cap

在这里插入图片描述

可以看到,除此之外,到在线服务器和到测试服务器的请求ip是不一样的。前者是192.168.119.1,而后者是转换后的192.168.2.254,也就是我们捏造的IP,注意伪造IP的时候,一定要避免环境中存在的IP和常用的IP。

注意
•辅助服务器要扮演成一个黑洞,所以不能开启ip_forward
•在请求会修改数据的地方,譬如修改数据库,如果配置不当,可能导致数据被重复修改多次。

6. 离线模式

需要在安装tcpcopy的时候,configure时加上–offline参数才能支持离线回放。除此之外,其他配置和在线模式一样。

cd tcpcopy-1.2.0
./configure --offline
make 
make install

使用tcpdump来抓包录制pcap离线文件

tcpdump -i eth0 -w online.pcap tcp and port 80

tcpcopy命令执行增加-i指定文件名

# /usr/local/tcpcopy/sbin/tcpcopy -x 80-192.168.119.152:80 -s 192.168.119.151 -c 192.168.2.254 -n 2 -d  -i ./online.pcap

可以观察到和在线模式同样的测试结果

GoReplay

GoReplay,原名叫gor,是一款go语言实现的简单安全的HTTP 实时流量复制的开源工具,用于捕获和回放实时HTTP流量到测试环境中,以便不断地用真实数据测试应用系统,可以用于增强对代码部署、配置更改和基础设施更改的信心。完美解决了 HTTP 层实时流量复制和压力测试的问题。
官方网站github地址,最新的版本为v0.16.1。

架构如下图所示:
在这里插入图片描述

从架构图可以看到,只需要http入口服务器上执行一个进程,就可以把生产环境的流量复制到任何地方,比如 Staging 环境、Dev 环境。

通过查看官网可以看到,开源版本的GoReplay只支持http,如果需要支持Thrift、protoBuffers等协议,需要购买GoReplay pro版本。
在这里插入图片描述

GoReplay 的功能
GoReplay支持流量的放大和缩小、频率限制,这样不需要搭建和生产环境一致的服务器集群也可以正确测试。GoReplay 还支持根据正则表达式过滤流量,这意味着可以单独测试某个 API 服务。还可以修改 HTTP 请求头,比如替换 User-Agent, 或者增加某些 HTTP Header 。

GoReplay还可以把请求记录到文件,以备回放和分析。GoReplay 支持和 ElasticSearch 集成,将流量存入 ES 进行实时分析。

GoReplay 的使用

  1. 输入参数
    –input-raw 捕获 HTTP 流量,需指定端口号
    –input-file 使用 --output-file 记录的文件作为输入
    –input-tcp 将多个 Gor 实例获取的流量聚集到一个 Gor 实例
  2. 输出参数
    –output-http 相应流量的终端,接收 URL 地址
    –output-file 将获取的流量记录如文件
    –output-tcp 将获取的流量转移至另外的 Gor 实例,与 --input-tcp 组合使用
    –output-stdout debug 工具,将流量信息直接输出
  3. 流量限制
    使用线上流量测试时,如线上流量过大,如果将全部流量导入演示(staging)或者 测试(dev)服务器,后果可想而知,GoReplay 流量限制策略为随机丢弃一部分请求,根据 Header 或者 URL 参数丢弃一部分请求,随机丢弃一部分请求,Absolute 如果当前秒请求达到限制,忽略剩余请求,直至下一秒计数器恢复Percentage 对于输入文件设定该参数将加速或者减缓请求执行速度。根据设定百分比使用随机数生成器决定请求是否被接受
    命令 Absolute 每秒最大 10 个请求数
    sudo ./goreplay --input-raw :8888 --output-http “http://t1:8888|10”
    命令 Percentage 获取少于 10% 的流量
    sudo ./goreplay --input-raw :8888 --output-http “http://t1:8888|10%”
    根据 Header 或者 URL 参数丢弃一部分请求
    命令 Limit based on header value
    sudo ./goreplay --input-raw :8888 --output-tcp “replay.local:28020|10%”
    –http-header-limiter “X-API-KEY: 10%”
  4. 请求过滤
    –http-allow-url 允许正则 URL,不包换主句名部分
    –http-disallow-url 不允许正则 URL
    –http-allow-header 允许的 Header 头
    –http-disallow-header 不允许的 Header 头
    –http-allow-method 允许的请求方法
  5. 命令举例
    简单的 HTTP 流量复制:
    goreplay -input-raw :80 -output-http “http://staging.com
    HTTP 流量复制频率控制:
    goreplay -input-tcp :28020 –output-http “http://staging.com|10
    HTTP 流量复制缩小:
    goreplay -input-raw :80 -output-tcp “replay.local:28020|10%”
    HTTP 流量记录到本地文件:
    goreplay -input-raw :80 -output-file requests.gor
    HTTP 流量回放和压测:
    goreplay -input-file “requests.gor|200%” -output-http “staging.com
    HTTP 流量过滤复制:
    goreplay -input-raw :8080 -output-http staging.com -output-http-url-regexp ^www.

GoReplay实验

1. 实验环境

线上服务器 --> 192.168.119.158
测试服务器 --> 192.168.119.152

2. 安装部署

在线上服务器上安装:

wget https://github.com/buger/goreplay/releases/download/v0.16.1/gor_0.16.1_x64.tar.gz
tar xzf gor_0.16.1_x64.tar.gz

解压后即为一个名叫goreplay的二进制可执行文件。

在两个服务器上启动一个简单的springboot程序,提供一个http接口,打印并返回hello world。

@RequestMapping("/hello")
private String service()  {
    LOG.info("hello world");
    return "hello world";
}
离线模式

在158上执行如下命令

./goreplay -input-raw :80 -output-file requests.gor

同样也在另外一台机器上,向线上服务器发起5次请求

这时候查看生成的requests.gor文件:
在这里插入图片描述

可以看到,生成的 requests_0.gor文件即为5个http请求报文。

当中线上服务器执行命令时

./goreplay -input-file=requests_0.gor -output-http="http://192.168.119.152:80"

在这里插入图片描述

可以看到这152服务器上的应用可以接受到请求。

在线模式

在158上执行如下命令

   ./goreplay  -input-raw :80  -output-http="http://192.168.119.152:80"

这时发送请求 curl http://192.168.119.1:80/hello
可以看到158和152上同时都收到的请求

猜你喜欢

转载自blog.csdn.net/wlmvp/article/details/85157659
今日推荐