您的位置:首页 > 理论基础 > 计算机网络

Linux下BICTCP实现上burst control机制分析

2010-04-11 18:06 183 查看

问题现象

在测试部发现的问题:测试SqlPlus的数据库查询速度的时候,发现经过我们连接代理后速度比不过代理时候慢一倍,在关闭我们的功能后1分钟能够完成查询,但是启用后就2分钟才能完成。

当时对SqlPlus的数据流还有TCP的抓包进行分析,该查询器查询数据的特点是客户端发出21个字节的数据包,然后服务器回应9356字节的数据,然后再由客户端发21字节的数据,再回应9356字节,如此重复直到查询完成。在没过代理时发现,9356字节的数据都是一瞬间送到客户端的,从而每次交互就是一个RTT的时间,而被代理后抓包发现这9356个字节都是分两次送出的,并不是一次性的全部发出,每次发出了6个包之后就开始等待对端的ACK,直到收到ACK后才发送剩下的字节,这样每一次交互就相当于耗费了两个RTT的时间,从而总时间多出了一倍。

摸索过程

在分析了SqlPlus的数据流后,写了一个简单的测试程序,因为SqlPlus拿来测试太麻烦了,每次都要登录等操作,极其繁琐。该测试程序工作方式:客户端发送m字节,然后服务端收到后回应n字节数据,如此反复,取m=21,n=9356用来模拟SqlPlus的数据情况,得到的现象是一样的,走代理后时间增加了一倍,后来继续加大该比例,当取到m=21,n=50000时发现过代理慢了2倍,从抓包分析是中间等了2次ACK。

那么是我们的代理程序引起的还是BICTCP的原因呢?

TCP的实现上算法有很多,但是影响发送性能,并且需要等对端ACK的只有2个值,拥塞窗口和对端的流控窗口。利用TCP提供的TCP_INFO套接字选项,把这些值全部取出来看,窗口是远超过6的,流控窗口方面,因为之前修改过TCP的缓冲区,所以这次把接收缓冲区修改为很大,但是问题依旧,利用netstat可以看到,TCP的Send-Q基本上都是几十K,说明大部分情况是有数据可以发送的,后来干脆把该测试程序直接在Linux上跑,也不经过代理,结果也发现比Windows下跑还是慢了1倍,从而可以肯定是与我们的业务程序代码是没有关系的,罪魁祸首肯定是Linux的TCP协议栈。

既然牵涉到协议栈,首先最大嫌疑的就是BICTCP,于是将/proc/sys/net/ipv4/bictcp=0,再跑,居然还是一样,看来又怀疑错对象了。

在应用层能做的事情就这么多了,还不能解决问题,那就要深入到内核里面去看代码了,是否有遗漏的东西,强调下要记得目标方向,内核里面的TCP代码相对比较复杂,机制非常多,很容易看走眼。

TCP在发包之前调用了一个函数来检测是否可以发送数据包:

代码int tcp_rcv_established(struct sock *sk, struct sk_buff *skb, struct tcphdr *th, unsigned len)
{
struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);

tp->bictcp_max_packets_in_flight = 0; /* reset burst moderation count */
……
}

int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb)
{
……
if (tp->bictcp_max_packets_in_flight == 0)
tp->bictcp_max_packets_in_flight = tcp_packets_in_flight(tp)+tcp_max_burst(tp);
……
}

也就是说在establish状态每收到任何一个包,bictcp_max_packets_in_flight就被置0了,然后每次发送一个包的时候再重新赋值为“in_flight”的数据包个数加上突发个数,其中tcp_max_burst(tp)是等于3的,那么在交互的过程当中,一旦收到对端的全部确认后,显然bictcp_max_packets_in_flight就为0了,那么下次再发送数据包的时候,bictcp_max_packets_in_flight就等于3了,当数据越发越多的时候,那么"in_flight"的数据也变大,所以bictcp_max_packets_in_flight也随之增大,但是当一次交互完成后,"in_flight"就为0了,也就相当于每一次交互都需要重新启动这个过程,因而就导致了之前看到的交互都变慢了的现象。

该控制机制的好处在于可以防止突然的大量数据:如果有一个包乱序或者丢失,后面接着就来了一大把数据,从而造成这个包一旦到达后,可以连续确认非常多的数据,如果没有此机制的话,那么便可以一次性的发出一大片数据,而使用了该方式后,则一次能发送的数据少的多,可以很大程度上的减少超时和包丢失率。

关于burst control的讨论可以在此看到:http://oss.sgi.com/archives/netdev/2004-07/msg00988.html,同时有数据分析,对比了burst control机制的影响:

http://www4.ncsu.edu/~rhee/export/bitcp/tiny_release/experiments/BIC-600-75-7500-1-0-0-noburst/index.htm

http://www4.ncsu.edu/~rhee/export/bitcp/tiny_release/experiments/BIC-600-75-7500-1-0-0/index.htm

建议解决方案

最简单的方式就是把/proc/sys/net/ipv4/bictcp_moderation置0,通过模拟测试可以确认该方法可以达到和不过代理时一样快的速度。

因为在2.6版本的内核代码里面找不到该限制了,在2.6的内核的发包函数里面只有对拥塞窗口、流控窗口和nagle算法的检查,没有burst的检查,在BIC的实现里面也没有该条件,可能某种原因把它去掉了,但是没有找到其说明,又或者用其他什么算法给替换了,暂时没有找到。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: