利用tcpcopy引流做模拟在线测试
上图中左边是线上前端机,右边是测试前端机。线上前端机开启tcpcopy客户端(tcpcopy进程),测试前端机开启tcpcopy服务端(interception进程),且两台机器上都启动了nginx服务。
Tcpcopy拷贝一次流量访问的步骤如下:
① 一个访问到达线上前端机;
② socket包在ip层被拷贝了一份传给tcpcopy进程;
③ tcpcopy修改包的目的及源地址,发给测试前端机;
④ 拷贝的包到达测试前端机;
⑤ 测试前端机的nginx处理访问,并返回结果;
⑥ 返回结果在ip层被截获、丢弃,由intercpetion拷贝返回结果的ip?header返回;
⑦ ip?header被发送给线上前端机的tcpcopy进程。
1.代码分析
1)?首先,在链路层或者IP层,在把包交到上一层之前,系统会检查有没进程创建了socket(AF_PACKET,SOCK_DGRAM,…) 或socket(AF_INET,SOCK_RAW,…)等类型的套接字(即原始套接字sock_raw),如果有,这个包就会被复制一份并发送到这个 socket的缓冲区。tcpcopy就是通过这种方式来复制访问流量的。上述的两种抓包方式,前者工作在数据链路层,后者工作在IP层。在 tcpcopy中不同版本所使用的抓包函数不同,在0.3版本中是:
int?sock?=?socket(AF_PACKET,SOCK_RAW,htons(ETH_P_IP));
而在0.4版本中,用的是:
int?sock?=?socket(AF_INET,SOCK_RAW,IPPROTO_TCP);
以上两个函数分别工作在链路层和IP层,前者会把进来和出去的包都抓取到,后者只 抓取到进来的包。
2) Tcpcopy在发送拷贝的数据包的时候,使用了如下socket:
sock?=?socket(AF_INET,?SOCK_RAW,IPPROTO_RAW);
并对这个socket设置了IP_HDRINCL:
setsockopt(sock,?IPPROTO_IP,?IP_HDRINCL,?&n,?sizeof(n));
因此网络层不会再增加ip?header.?发送之前更改了包的目的ip和端口:
tcp_header->dest?=?remote_port;
ip_header->daddr?=?remote_ip;
最后调用sendto函数发送包到测试前端机:
send_len?=?sendto(sock,(char?*)ip_header,tot_len,0,
(struct?sockaddr?*)&toaddr,sizeof(toaddr));
3)?在测试前端机上加载了ip_queue模块,并设置iptables规则:
iptables?-I?OUTPUT?-p?tcp?–sport?80?-j?QUEUE
复制的访问流量到达测试前端机上的nginx,nginx处理并返回结果,这个结果包在IP层会被前面所设置的iptables规则匹配发往目标 (target)QUEUE。而QUEUE是由ip_queue模块实现。下一步这个匹配包就会被内核经过netlink?socket发往用户空间的程 序(在这是tcpcopy的服务端interception进程)。
netlink?socket是内核与用户进程之间的一种通信机制,是网络应用程序与内核通信的最常用的接口,可以用来配置网络的各个方面(比如包的过滤)。
interception用如下方式创建netlink?socket:
int?sock?=?socket(AF_NETLINK,SOCK_RAW,NETLINK_FIREWALL);
NETLINK_FIREWALL协议有三种消息类型:IPQM_MODE,IPQM_PACKET,IPQM_VERDICT.
内核通过一个IPQM_PACKET消息将刚才截获的返回结果包发送到interception,interception给内核发送一个 IPQM_VERDICT消息告诉内核对这个包的裁决结果(DROP,ACCEPT,etc.)。tcpcopy通过这样的办法将测试前端机上nginx 返回的结果截获丢弃,并由interception返回一个ip?header.相应代码实现如下:
拷贝结果包的ip?header,发送:
所有线上前端机都开启tcpcopy客户端,由于一直报”Message?too?long”(这是由于packets长度超过1500造成,每分钟差不多有50个)刷屏,所以将stderror重定向,
sudo?./tcpcopy?ipA?80?ipB?80?2>/dev/null?&
在测试前端机上开启tcpcopy服务端程序interception,并设置iptables规则。
压了大约有一个星期,期间观察qps,load等各项指标是否正常。新引擎单个集群一天的平均qps大约是110,峰值大约240。实验结果显示的 包丢失率大约是(1822213-1797242)/1822213=1.37%.?后来进一步将多个线上前端机的流量引到一个测试前端,测试新引擎的单 集群极限服务能力,qps能达到1000以上,?latency大约40ms,达到了上线要求。
Tcpcopy客户端和服务端本身占用的资源较少,不影响在线服务。
13991?root??????20???0??160m??77m??888?R??7.7??0.3??71:26.24?tcpcopy
7723?root??????15???0?42592??38m??324?S??5.8??0.2??12:14.83?interception
%cpu分别占7.7%和5.8%,物理内存占用分别是77m和38m.
由于几乎完全模拟了线上环境,我们对于新引擎上线更有信心,最终上线圆满成功,实现平稳过渡。现在利用tcpcopy拷贝线上流量作模拟压测已成为我们日常开发上线流程中的一项内容。
六、附录
项目主页:http://code.google.com/p/tcpcopy/;
Sock_raw:http://sock-raw.org/papers/sock_raw;
Netlink:http://smacked.org/docs/netlink.pdf;
相关主题:http://blog.csdn.net/wangbin579/article/category/926096/1 ;
代码svn地址:http://tcpcopy.googlecode.com/svn/trunk;