分布式TCPCOPY架构、压测

基于server的请求回放领域,一般分为离线回放和在线实时复制两大领域,一般研究者都是从离线回放的角度在苦苦研究,而在实时复制领域,研究非常少,至少从sigcomm评审人的评审意见来看,没有看到相关内容。

请求实时复制,据我所知,一般可以分为两类:

1)基于应用层的请求复制
2)基于底层数据包的请求复制

传统的做法一般从应用层面进行复制,比如基于服务器的请求复制,这种复制的好处就是实现起来相对简单,但也存在着若干缺点:
1)请求复制从应用层出发,穿透整个协议栈,这样就容易挤占应用的资源,比如宝贵的连接资源
2)测试跟实际应用耦合在一起,容易导致对在线系统的影响,比如有些基于服务器的复制,会导致用户请求的处理时间取决于最慢的请求处理时间(max(真正的请求处理时间,被复制的请求请求处理时间))
3)很难支撑压力大的请求复制(据若干用户反映,这种类型的请求复制,曾经严重影响在线系统)
4)很难控制网络延迟

基于底层数据包的请求复制,可以做到无需穿透整个协议栈,路程最短的,可以从数据链路层抓请求包,从数据链路层发包,路程一般的,可以在IP层抓请求包,从IP层发出去,不管怎么走,只要不走TCP,对在线的影响就会小得多。


因此从数据包的角度去做基于server的请求复制,方向是对的,而且潜力非常巨大,很可惜,tcpreplay的作者做了一点这方面的探索(flowreplay),就放弃了。这方面的研究至少我没有看到过(一般都去研究整个网络了,sigcomm评审人也没有提出类似的研究方案)。

进入正题,tcpcopy是如何进行架构演化的呢?

tcpcopy架构已历经三代,基本原理都一样,本质是利用在线数据包信息,模拟tcp客户端协议栈,欺骗测试服务器的上层应用服务。由于tcp交互是相互的,一般情况下需要知道测试服务器的响应数据包信息,才能利用在线请求数据包,构造出适合测试服务器的请求数据包,因此只要基于数据包的方式,无论怎么实现(除非是tcp协议改的面目全非),都需要返回响应包的相关信息。

三种架构的差别就在于在什么地方截获响应包

我们先看看tcpcopy最初的架构:

分布式TCPCOPY架构、压测

从上图可以看出,tcpcopy是从数据链路层(pcap接口)抓请求数据包,发包是从IP层发出去,测试服务器的TCP协议栈没有类似ip queue或者nfqueue的干扰,响应包会直接返回给在线机器(通过设置路由),tcpcopy可以在数据链路层捕获到这些响应包,这些响应包会到达IP层,一般最终被丢弃掉(除非是客户端IP地址就是这台在线机器的IP地址,会通过IP层,但会被TCP reset掉)。

这种架构一般只能工作在同一网段,而且对于外网应用,一般只能复制单台在线流量给测试服务器,无法对系统进行深度问题发现和潜能挖掘。



第一种架构总结如下:
好处:
1)简单,粗暴
2)适合冒烟测试
3)测试结果比较真实


不好的地方:
1)相对而言,会更加影响在线,因为响应包信息全部回给在线机器了(当然这种还是比应用层面的请求复制,影响更小)
2)同一网段限制
3)对于外网应用,无法充分利用或者很难充分利用多台在线流量,从而无法为压力测试提供技术支持
4)内网应用严重受限制,因请求的客户端IP地址不能是被复制的在线机器的IP地址

第二种架构,也就是目前开源的架构,大致架构如下:

分布式TCPCOPY架构、压测

从上面图中我们可以看出,tcpcopy默认从IP层抓包,从IP层发包,与第一种架构不同的是,我们在测试服务器进行响应包的截获,并通过intercept程序返回响应包的必要信息给tcpcopy。这种架构为分布式压力测试提供了可能性,相比第一种架构,大大推动了tcpcopy的进化。

我们先从响应包的截获来分析,理论上,可以在测试服务器的IP层或者数据链路层进行截获响应包,我们具体分析如下:

1)在数据链路层抓,正常情况下,其响应数据包会返回给真正发起请求的客户端,这会或多或少影响到客户端的TCP(频繁地reset)模块,而且在压力大的时候,会给交换机、路由器甚至整个网络,带来不必要的干扰。

2)在测试服务器的IP抓响应包,正好有netlink技术来解决上面的问题,netlink是一种用户态进程与内核进行交互的技术,具体地我们可以利用内核模块ip queue(内核3.5以下版本)或者nfqueue(内核3.5或者以上版本)来达到捕获响应包的目的。

我们采用了第二种方式,也即上图中的IP层来截获响应包,当响应包传递给intercept后,我们就能copy到响应包信息的必要信息(一般为TCP/IP头部信息),传递给tcpcopy,我们还可以通过verdict告诉内核,该如何处理这些响应包,如果没有设置白名单的话,就会在IP层丢弃掉这些响应包,这时候你是无法利用tcpudmp来抓到这些响应包的(tcpdump工作在数据链路层)。


这种设计的好处就是可以支持复制多台在线流量到一台测试服务器中去,我们在intercept保留路由信息,知道响应包的相关信息该如何返回给哪一个tcpcopy实例。然而这种架构,intercept会不同程度地占用测试服务器的资源,而且ip queue或者nfqueue,并不一定能够高效工作,因而给测试,特别是高压测试和短连接压力测试,带来了很大麻烦。

这种架构总结如下:
好处:
1)支持复制多台在线流量
2)影响在线机器更小,因为一般只需要返回TCP/IP头部信息

不好的地方:
1)较第一种更为复杂
2)性能极限往往在ip queue或者nfqueue
3)intercept扩展性不好,受制于ip queue和nfqueue无法支持多进程进行响应包的捕获操作
4)intercept影响测试服务器的最终测试结果,特别是压力大的时候
5)无法对测试服务器进行完整测试(没有覆盖到数据链路层的出口)
6)运维不方便

第三种架构,如下图:

分布式TCPCOPY架构、压测

上述架构,也即最新架构,是为了极限测试的目的而设计的,把intercept的工作从测试服务器(test server 1)中offload出来,放到另外一台独立的测试服务器(test server 2)上面进行截获响应包,而且把原先从IP层捕获响应数据包的工作转移到从数据链路层抓响应包,这些改变大大降低了对测试机器的各种干扰(除了路由设置,其它已经没有影响了),而且大大扩大了捕获响应包的能力。当然这种测试也更加真实。

具体如下:

在运行上层服务的测试服务器test server 1上面设置路由信息,把待测试应用的需要被捕获的响应数据包信息路由到第二台测试机器test server 2上面,在测试机器test server 2上面,我们在数据链路层截获到响应包,从中抽取出有用的信息,再返回给相应的tcpcopy。

为了高效使用,这种架构推荐使用pcap进行抓包,这样就可以在内核态进行过滤,否则只能在用户态进行包的过滤,而且在intercept端或者tcpcopy端设置filter(通过-F参数,类似tcpdump的filter),达到多个实例来共同完成抓包的工作,这样可扩展性就更强,适合于超级高并发的场合。

这种架构需要的机器资源也更多,而且也变得更加难使用,需要了解tcp知识,route知识和pcap filter知识(类似于tcpdump过滤条件),因此推荐有条件的并且熟悉上述知识的人使用最新的架构。

总结如下:
好处:
1)更加真实
2)可扩展性更强
3)适合高并发场合
4)无ip queue或者nfqueue的各种限制
5)对测试服务器几乎没有任何性能干扰的影响
6)在运行服务的测试服务器,运维更加方便
7)不会随运行服务的服务器崩溃而崩溃

不好的地方:
1)操作难度更大
2)需要的机器数量更多
3)需要的知识也更多

上面三种架构均具有价值,目前开源出来的仅仅包括第二种架构和第三种架构,tcpcopy默认采用第二种架构,有条件的可以采用第三种架构。

对于请求复制,要想达到对在线没有影响或者影响尽可能小,可以采用如下对策:

利用高性能的旁路机制,复制请求数据包到另外一个独立的系统,在这个独立的系统,我们采用第三种架构,进行请求的捕获,再复制给测试服务器上面的应用。

对于如何采用新架构?

下载tcpcopy

configure:

./configure --enable-advanced --enable-combined --enable-pcap

运行方法参考下面具体例子:

这是一个内网的应用例子,我们的目的是复制下图中adserver应用服务器的请求到测试系统中去。

分布式TCPCOPY架构、压测

在线adserver有2台,主要供nginx调用,所以客户端IP地址来自于nginx所在机器的IP地址,均为同一网段的IP地址。
我们假设在线adserver机器为10.100.10.1,10.100.10.2,nginx所在的机器ip地址为:10.100.10.11,10.100.10.12,10.100.10.13,
测试服务器有10.100.10.31,10.100.10.32
其中,10.100.10.31运行着类似在线adserver的应用,端口为11511,而在线应用端口是11311
我们在10.100.10.31上面添加如下路由:
route add -host 10.100.10.11 gw 10.100.10.32
route add -host 10.100.10.12 gw 10.100.10.32
route add -host 10.100.10.13 gw 10.100.10.32

这里的意思就是说,在测试服务器10.100.10.31返回给客户端10.100.10.11~13的响应走默认网关10.100.10.32,但10.100.10.32机器其实并没有开启路由模式,所以这些响应包到了10.100.10.32机器后,会在ip层被drop掉,留给我们的机会就是可以在10.100.10.32的数据链路层抓到这些响应包。

我们在10.100.10.32机器上面运行intercept,用来捕获响应包,命令如下:

    configure选项:
    ./configure --enable-advanced --enable-combined --enable-pcap 
    (enable-advanced代表采用新架构,enable-combined合并响应包,-enable-pcap代表pcap抓包,如果libpcap库为1.0.0以下版本,则最好升级到1.0.0以上版本)
    执行intercept命令(需要root权限):
    ./intercept -i eth0 -F 'tcp and src port 11511' -d
    如果测试端口和在线端口一样的话(11311),需要进一步过滤
    ./intercept -i eth0 -F 'tcp and src host 10.100.10.31 and src port 11311' -d
    
我们在在线机器上面运行tcpcopy(root权限):
     configure选项:
         ./configure --enable-advanced --enable-combined --enable-pcap
         
         ./tcpcopy -x 11311-10.100.10.31:11511 -s 10.100.10.32 -i eth0 -d
         这里面采用pcap抓包,因为11311和11511端口不同,就不需要设置filter了,tcpcopy自动就构造有效的filter。
         

         这里tcpcopy的含义是复制在线11311端口的数据包到10.100.10.31上面的11511端口中去,-s指定运行intercept所在机器的ip地址,-i指定从eth0接受请求数据包。

         如果在线应用端口和测试应用端口一样,那么命令应该这样:
            在线机器10.100.10.1
            ./tcpcopy -x 10.100.10.1:11311-10.100.10.31:11311 -s 10.100.10.32 -i eth0 -d    
           在线机器10.100.10.2
           ./tcpcopy -x 10.100.10.2:11311-10.100.10.31:11311 -s 10.100.10.32 -i eth0 -d    

        或者进一步设置filter
          在线机器10.100.10.1
          ./tcpcopy -x 11311-10.100.10.31:11311 -F 'tcp and dst port 11311 and dst host 10.100.10.1' -s 10.100.10.32 -i eth0 -d    
          在线机器10.100.10.2
          ./tcpcopy -x 11311-10.100.10.31:11311 -F 'tcp and dst port 11311 and dst host 10.100.10.2' -s 10.100.10.32 -i eth0 -d    


这样请求就过去了,测试结果如下:

        
      # grep 'Thu 10:30' access_0516_10.log -c       
        99415
      # grep 'Thu 10:30' access_0516_10.log -c  
        99414
     # grep 'Thu 10:30' access_0516_10.log -c
        198693
        
        10.100.10.1机器上面的error_tcpcopy.log:
        2013/08/6 14:05:06 +553 [notice] active:110,rel reqs:29116,obs del:16468
        2013/08/6 14:05:06 +553 [notice] conns:29121,resp packs:23787879,c-resp packs:23697217
        2013/08/6 14:05:06 +553 [notice] send Packets:44041861,send content packets:20270532
        2013/08/6 14:05:06 +553 [notice] reconnect for closed :0,for no syn:3511
        2013/08/6 14:05:06 +553 [notice] retransmit:24
        2013/08/6 14:05:06 +553 [notice] successful retransmit:24
        2013/08/6 14:05:06 +553 [notice] syn cnt:25715,all clt packs:32573317,clt cont:20297550
        2013/08/6 14:05:06 +553 [notice] total captured pakcets:32573317
        

        从日志文件来看,从pcap接口抓的包都是有用的数据包(total captured pakcets==all clt packs),并没有抓到其它应用的数据包,从send content packets和clt cont的数值相比,也可以看出绝大部分请求都过去了。

最后,需要注意新架构的若干细节:
1)发起请求的客户端所在机器,不能同时运行相应的intercept,因为响应数据包路由回来后,这台机器的tcp层会发送reset数据包给测试服务器,这样就会干扰测试的进行。
2)在线服务和测试服务不能同时在一台机器
      由于在线服务响应的目的ip地址和测试服务响应的目的ip地址是一样的,所以路由设置的时候,无法区分在线的响应和测试的响应
3)当在线应用服务端口和测试端口一样时,如果采用pcap抓包,需要指明详细的过滤条件,否则pcap会抓到tcpcopy发出的数据包,导致抓包效率低下
4)当采用pcap抓包时,最好设置具体的网卡(通过-i参数),这样可以减少pcap库(1.0.0以上版本)所占的buffer,而且性能会更高
5)对于外网应用,由于客户端ip地址来自于世界各地,因此最好有两个网卡,一个外网网卡,一个内网网卡,让外网请求都路由到第二台测试服务器上面去
   比如改变测试服务器上面的默认路由:
   route del default gw 真正的网关ip地址

   route add default gw 第二台测试服务器的ip地址

6)如果是在同一网段利用外网地址访问,在机器B上面设置去往机器A的响应,走机器C,那么设置默认外网网卡路由不会生效,需要显式指定,比如:

   route add -host 机器A的外网ip地址  gw 机器C的外网ip地址

相关推荐