并发连接同一地址超时问题

今天和duolong一起追查connect超时问题,结果发现是arp pending queue len的原因;

1. 现象描述
    现象a:自己写的client程序,开启16个socket并发connect同一server(异步框架,超时时间为2s),结果其中3个socket连接成功,其余13个超时失败;
    现象b:一次试验后,接下来多次连续试验都会成功(不出现超时现象);
    现象c:停止试验一段时间后,再次试验,现象同b;
    现象d:每次试验前,主动删除相应server的arp条目(arp -d server-ip),问题稳定复现;
    现象e:抓包结果如下,arp请求成功后,3个socket的syn包立刻发出;13个socket的syn包,3s后才发出;
           同一client并发connect同一server超时失败问题

2. 原因追查
    根据上述现象,查看2.6.32源码,围绕arp相关(neighbour)的代码,发现arp模块有个queue(neigh->parms->queue_len)长度默认为3,该队列用于存储arp尚未成功pengding的skb,如,我们试验中的syn包;如果因arp pending的skb超过3,则arp模块会丢弃queue中老的skb。
    源码路径:neigh_resolve_output()->neigh_event_send()->__neigh_event_send()
    源码片段:
    if (neigh->nud_state == NUD_INCOMPLETE) {
        if (skb) {
            if (skb_queue_len(&neigh->arp_queue) >=
                neigh->parms->queue_len) {
                struct sk_buff *buff;
                buff = __skb_dequeue(&neigh->arp_queue);
                kfree_skb(buff);
                NEIGH_CACHE_STAT_INC(neigh->tbl, unres_discards);
            }
            __skb_queue_tail(&neigh->arp_queue, skb);
        }

     分析:16个并发connect的syn包,由于arp失效/重新请求,只有最后的3个保存在queue中(与上图中成功的syn包端口号最大的现象吻合),其余13个被丢弃,tcp层3s后重传13个syn包(netstat -s | grep timeout,超时重传计数增大13);另外,查看“NEIGH_CACHE_STAT_INC(neigh->tbl, unres_discards”)相关的ures_discards计数也增大13;

     相关参数:neigh->parms->queue_len 可以通过/proc/sys/net/ipv4/neigh/eth0/unres_qlen调节大小;
                unres_discards计数可以通过/proc/net/stat/arp_cache (unresolved_discards的值)查看(注意该计数是per cpu的);
              
3. 解决方法
    增大arp pending queue len, echo 32 > /proc/sys/net/ipv4/neigh/eth0/unres_qlen;

4. 快速复现方法
    下面提供一个简单的复现上述问题的方法;
    第一步:删除相应arp cache, 命令: arp -d server-ip
    第二步:发送多个数据包长度的ping,命令:ping -c 2 -s 8000 server-ip

原文链接:http://blog.sina.com.cn/u/2015038597

posted @ 2017-06-27 16:01  永志  阅读(743)  评论(0编辑  收藏  举报