丢包问题研究

丢包问题研究

Linux服务器丢包故障的解决思路及引申的TCP/IP协议栈理论

Ring Buffer 溢出

从底向上来看 Linux 接收数据包的处理过程,首先是网卡驱动层。如下图所示,物理介质上的数据帧到达后,首先由 NIC(网络适配器)读取,写入设备内部缓冲区 Ring Buffer 中,再由中断处理程序触发 Softirq 从中消费,Ring Buffer 的大小因网卡设备而异。当网络数据包到达(生产)的速率快于内核处理(消费)的速率时,Ring Buffer 很快会被填满,新来的数据包将被丢弃。

linux-server-packet-fig

确认方法:

  • ethtool -S eth0|grep rx_fifo
  • cat /proc/net/dev

通过观察接收方向的 fifo 丢包数是否有增加来确定;

解决办法:如果发现服务器上某个网卡的 fifo 数持续增大,可以确认一下 CPU 中断是否分配均匀;也可以尝试增加 Ring Buffer 的大小;

  • 查看 eth0 网卡 Ring Buffer 最大值和当前设置
$ ethtool -g eth0
  • 修改网卡 eth0 接收与发送硬件缓存区大小
$ ethtool -G eth0 rx 4096 tx 4096

netdev_max_backlog 溢出

netdev_max_backlog 是内核从 NIC 收包后,交由协议栈(如IP、TCP)处理之前的缓冲队列。每个 CPU 核都有一个 backlog 队列,与 Ring Buffer 同理,当接收包的速率大于内核协议栈处理的速率时,CPU 的 backlog 队列不断增长,当达到设定的 netdev_max_backlog 值时,数据包将被丢弃。

确认方法:通过 cat /proc/net/softnet_stat 可以确定是否发生了 backlog 队列溢出;

[root@wg-esm-hc-1 ~]# cat /proc/net/softnet_stat
0002562c 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
000851b8 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
0007d457 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
000111fb 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
000278e5 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
000351a9 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
0001fcd4 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00046236 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00001209 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000bb8 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000701 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000eb2 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
000016d8 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000d92 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000a80 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000ec3 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00022569 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
000523aa 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
0002f95c 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00059baa 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00061fec 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
000549bf 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
0009e672 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
000a5f80 00000000 00000001 00000000 00000000 00000000 00000000 00000000 00000000 00000000
000005cc 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000b1d 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000a78 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000a63 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
0000079c 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
000007ea 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000641 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
0000058e 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
[root@wg-esm-hc-1 ~]#

每一行代表每个 CPU 核的状态统计,从 CPU0 依次往下; 每一列代表一个 CPU 核的各项统计:第一列代表中断处理程序收到的包总数;第二列代表由于 netdev_max_backlog 队列溢出而被丢弃的包总数。

解决办法:netdev_max_backlog 的默认值是 1000 ,在高速链路上,可能会出现上述第二列统计不为 0 的情况,可以通过修改内核参数 net.core.netdev_max_backlog 来解决:

Linux系统网卡中断瓶颈丢包的解决思路

现在机器硬件发展快,硬件价格逐步下降,使用到多核CPU多队列网卡越来越多。

当需要把比较好的硬件发挥到较高水平的时候,需要考虑网卡中断均衡的问题。

特别是还在使用 2.6.3x 版本及其以下的 Linux 内核时,机器的流量很容易造成网卡中断不均衡而产生丢包

问题表现(经验):

  • 千兆网卡,跑到 600Mbps 的时候出现机器丢包;
  • 万兆网卡,跑到 3Gbps 的时候出现丢包;
  • CPU 出现单核的 si 使用率基本 100% ;

如果问题原因是“CPU 出现单核的 si 使用率基本 100%”导致,则

  • 通过命令 cat /proc/interrupts | grep ethX 查看和指定接口相关的系统中断信息;
  • 通过均衡网卡的中断处理到多核上,充分利用多核的性能来解决该问题;
  • 通过手动配置 /proc/irq/{NUM}/smp_affinity 可以实现绑定中断号到某个核上面;
  • 关闭 irqbalance 服务,避免其干扰手动调整中断的 CPU 亲和值;

可用脚本:


用Wireshark解决疑难杂症

抓包的原理

我们常常使用 tcpdumpWireshark 进行抓包,其底层依赖于 libpcap(Windows 下为 winpcap),其抓包过程大致如下:

  1. 网卡加载初始化驱动;
  2. 报文通过网络到达网卡;
  3. DMA 将报文复制到内核内存(Ring Buffer)中;
  4. 复制完毕后,网卡产生硬中断通知系统;
  5. NAPI 处理内核报文;
  6. libpcap 将获取的原始报文复制一份;
  7. 使用设置的过滤器对报文进行过滤,并保存至缓存中;
  8. 应用程序(TcpdumpWireshark)读取缓存中的数据。

丢包原因

为什么会丢包呢?系统缓存是一个 ring buffer,用来缓冲消息提高可靠性。当 buffer 满时,最新的数据会自动覆盖最老的数据,导致数据报文丢失。

解决方法

可以从三个方面着手去降低丢包的可能性:

  1. 根据策略对报文进行负载均衡,降低单台服务器报文数量;
  2. 提高报文处理速度,如 ring0 数据直接映射到 ring3 ,绑定 CPU 与终端等;
  3. 增大缓存的大小;

在实际情况中,我们常用的做法是 2 与 3 。

提高报文处理速度

推荐使用 PF_RING ,它通过优化 buffer 和 CPU 提高处理效率。

在抓包方案选择上,建议是根据流量大小选择不同的抓包方式:

  • 50Mbps 以下,使用原生的 tcpdump 抓包;
  • 50~1000Mbps 之内,使用加载了 PF_RING 驱动的 tcpdump 进行抓包;
  • 1Gbps 以上,推荐旁路抓包,使用 PF_RING ZC 驱动代替原有网卡驱动,再使用加载了 PF_RING 驱动的 tcpdump 抓包;
提高缓存大小
  • 调整网卡驱动的 Ring Buffer :
ethtool -g p5p1  // 查看
ethtool -G p5p1 rx 4096  // 设置
  • 设置 tcpdump 缓存
tcpdump  -B // 设置 buffer size

网络分析技术之葵花宝典

看一下 tcpdump 程序调用模型:

tcpdump程序调用模型

可见,tcpdump 和一般的网络程序完全不同,它依赖的是 libpcap ,而 libpcap 使用的是一种称为**设备层的包接口(packet interface on device level)**的技术。使用这种技术,应用程序可以直接读写内核驱动层面的数据,而不经过完整的 Linux 网络协议栈。

tcpdump 与 iptables 的关系

可能会有疑问,如果一种输入的网络通信(INPUT)被 iptables 给禁止了,那么 tcpdump 还可以抓取到吗?

答案是肯定的。

  • tcpdump 直接从网络驱动层面抓取输入的数据,不经过任何 Linux 网络协议栈。
  • iptables 依赖 netfilter 模块,后者工作在 Linux 网络协议栈中;

因此:

  • iptables 的入栈的策略不会影响到 tcpdump 抓取;
  • iptables 的出栈策略会影响数据包发送到网络驱动层面,因此,出栈策略会影响到 tcpdump 的抓取;

tcpdump 和 iptables 的关系,总结下来就是:

  • tcpdump 可以抓取到被 iptables 在 INPUT 链上 DROP 掉的数据包;
  • tcpdump 不能抓取到被 iptables 在 OUTPUT 链上 DROP 掉的数据包;

丢包原因汇总

在高速复杂网络环境下,可能导致丢包的各种原因:

  • NAPI 之前的 Linux NIC 驱动总是在接收到数据包后立即触发中断进行处理,故 Cpu 处于频繁中断状态,造成接收数据包效率低下;
  • 基于传统 Linux 内核网络协议栈,数据包的处理会经过两次拷贝(polling),从网卡驱动到内核,再从内核到用户空间,浪费了大量时间和资源。
  • 机器的流量很容易造成网卡中断不均衡而产生丢包;当网卡中断全部打在一个核上时,会造成单核处理网卡中断的情况,当该单核达到瓶颈后(top 的 si 指标)就会开始丢包;
  • pcap_next occasionally losing packets on Linux
  • 数据包节流 (Packet throttling),NAPI 之前的 Linux NIC 驱动总是在接收到数据包之后产生一个 IRQ ,接着在中断服务例程里将这个 skb 加入本地的 softnet ,然后触发本地 NET_RX_SOFTIRQ 软中断后续处理。如果包速过高,因为 IRQ 的优先级高于 SoftIRQ ,导致系统的大部分资源都在响应中断,但 softnet 的队列大小有限,接收到的超额数据包也只能丢掉,所以这时这个模型是在用宝贵的系统资源做无用功。而 NAPI 则在这样的情况下,直接把包丢掉,不会继续将需要丢掉的数据包扔给内核去处理,这样,网卡将需要丢掉的数据包尽可能的早丢弃掉,内核将不可见需要丢掉的数据包,这样也减少了内核的压力。
  • NIC Ring Buffer 溢出(rx_fifo);
  • per-CPU backlog queue(softnet)溢出(netdev_max_backlog);
  • tcpdump 缓存溢出;

经验汇总

  • Vanilla Linux 处理速度仅能达到约 1M pps
  • 现代 10Gbps 网卡的处理能力通常能够达到至少 10M pps
  • (经验值)千兆网卡,跑到 600Mbps 的时候出现机器丢包;
  • (经验值)万兆网卡,跑到 3Gbps 的时候出现丢包;
  • (经验值)CPU 出现单核的 si 使用率基本 100% ;
  • 通常的做法是:将网络流量通过交换机旁路镜像的方式送到服务器,然后在服务器上基于 tcpdump 方式进行抓包,后续再通过 tcpdump 进行过滤查找;但这种方式在流量特别大的时候也会遇到一些问题;
  • 流量在 50Mbps 以下,使用原生的 tcpdump 抓包;
  • 流量在 50~1000Mbps 之内,使用加载了 PF_RING 驱动的 tcpdump 进行抓包;
  • 流量在 1Gbps 以上,推荐旁路抓包,使用 PF_RING ZC 驱动代替原有网卡驱动,再使用加载了 PF_RING 驱动的 tcpdump 抓包;

转载于:https://my.oschina.net/moooofly/blog/884438

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值