系统服务器应用UDP分析丢包问题的思路
在工作中,遇到了一些服务器应用的UDP丢包问题。在排查问题的过程中,查阅了很多资料,总结了这篇文章,以便更多的人参考。
在开始之前,我们先用一张图来解释一下Linux中接收网络数据包的过程。
- 首先,网络数据包通过物理网线发送到网卡。这个过程使用DMA(直接内存访问),不需要CPU参与
- 内核从环形缓冲区中读取消息进行处理,执行IP和TCP/UDP层逻辑,最后将消息注入到应用程序中。 socket buffer
- 应用程序从socket buffer中读取消息进行处理
![]()
在接收UDP消息的过程中,图中的任何进程都可以主动或被动地丢弃消息,因此数据包可能在网卡和控制器上丢失,或者在系统和应用程序中。
之所以不分析发送数据的过程,是因为发送的过程和接收的过程类似,只是方向相反;另外,发送过程中消息丢失的概率比接收过程中的要小。仅当应用程序发送消息的速率快于内核和网卡的处理速度时。它仅以一定的速度发生。
本文假设计算机只有一个名为 eth0 的接口。如果有多个接口或者接口名称不是eth0,请根据实际情况进行分析。
注:文中RX(接收)表示接收消息,TX(发送)表示发送消息。
确认 UDP 丢包
如果想检查网卡是否丢包,可以使用 ethtool -S eth0 检查并在输出中搜索
![]()
drop 相关字段是否有数据。正常情况下,这些字段对应的数字都应该为0。如果看到对应的数字不断增加,则说明网卡正在丢包。另外一个检查网卡包数据的命令是Ifconfig,它的输出会有Rx(Receive Accept Match)和❀Tx‼等)统计:
![]()
另外, Linux系统还提供了各个网络协议的丢包信息,可以使用命令 对于以上输出,注意以下信息查看UDP丢包: 如前所述,如果 如果没有硬件或驱动问题,网卡丢包一般都太小。您可以使用命令 默认是该网卡的最大缓冲区值,可以使用 Linux 丢包的原因有很多。常见的包括:UDP报文错误、防火墙、UDP缓冲区大小不足、系统负载过大等。下面我们就分析一下这些丢包的原因。 如果UDP报文在传输过程中被修改,会导致校验和错误或长度错误。 Linux 在收到 UDP 消息时验证这一点。当发现错误时,该消息将被丢弃。 如果希望在出现错误时及时将UDP消息校验和发送给应用程序,可以通过socket参数禁用UDP校验和检查: 如果系统防火墙丢包,行为一般都是UDP无法正常接收数据包。当然,也不排除防火墙只丢弃部分数据包的可能性。 如果您遇到非常高的丢包率,请先检查您的防火墙规则,以确保防火墙没有主动丢弃 UDP 数据包。 Linux收到消息后,将消息存储在缓冲池中。由于缓冲池的大小是有限的,如果UDP数据包太大(超过缓冲池大小或MTU大小)或者数据包接收率太高,Linux可能会因为缓冲区已满而直接丢包。 在系统级别,Linux 设置了可以为接收缓冲区配置的最大值,您可以在下面的文件中看到。一般来说,Linux在启动时根据内存大小设置一个初始值。 但是不建议使用这些初始值处理大流量的UDP消息。 ,如果应用程序接收和发送大量UDP数据包,则需要增大该值。可以使用 sysctl 命令使其立即生效: 也可以在 /et/ 中调整对应值sysctl.conf 如果消息太大,可以在发送方对数据进行拆分,以确保每条消息的大小在 MTU 之内。 另一个可配置的参数是 以及 CPU、内存、IO负载过高,会导致网络丢包。例如,如果CPU负载过高,系统没有时间执行校验和计算、将数据包复制到内存等操作,导致网卡或套接字缓冲区中的数据包丢失; 如果内存负载过高,会导致应用进程过慢,无法及时处理消息; IO 负载过高。 CPU用于响应IO等待,没有时间处理缓冲区中的UDP消息。 Linux系统本身就是一个互联的系统。任何一个部件出现问题都会影响其他部件的正常运行。 如果系统负载过高,要么是应用程序有问题,要么是系统缺陷。前者必须及时发现、调试、修复;后者必须及时发现和扩展。 如上所述,系统UDP缓冲区大小被修改。修改后的sysctl参数只是系统允许的最大值。每个应用程序在创建套接字时都必须设置自己的套接字缓冲区大小值。 Linux系统会将接收到的消息存储在socket缓冲区中,应用程序会不断地从缓冲区中读取消息。因此有两个与应用程序相关的因素会影响数据包是否丢失:套接字缓冲区的大小和应用程序读取数据包的速度。 在第一个问题中,您可以在应用程序初始化套接字时设置套接字的接收缓冲区大小。例如,以下代码将套接字缓冲区设置为 20 MB: uint64_t receive_buf_size = 20*1024*1024; //20 MB setsockopt(socket_fd, SOL_SOCKET, SO_RCVBUF, &receive_buf_size, sizeof(receive_buf_size)); 如果你不自己编写和维护程序,修改应用程序代码是不好的,甚至是不可能的。 许多应用程序提供配置参数来调整该值。参见相关官方文档;如果没有可用的配置参数,您只能向程序开发人员提出问题。 显然,增加应用程序的接收缓冲区会减少丢包的可能性,但也会导致应用程序使用更多的内存,所以要谨慎使用。 另一个因素是应用程序读取缓存消息的速度。对于应用程序来说,消息处理应该是异步的 想要了解更多有关 Linux 执行的信息 如果在任何功能中丢包,您可以使用 通过这些信息找到对应的内核代码,就可以知道内核是在哪一步丢包的,以及丢包的大概原因。 另外,还可以使用Linux perf工具来追踪 网上有很多文章介绍perf命令的使用和解释你可以去看他。 netstat -s plus --udp 只能显示UDP相关的报文数据: 下次启动时该参数仍然有效。 ![]()
网卡或驱动包丢失
ethtool -S eth0包含 rx_***_errors,那么很可能是 rx_* *的问题*_errors网卡导致丢包,需要联系服务器或网卡厂商处理。 ![]()
netstat -i 还将提供有关每个网卡接收和丢失的数据包的信息。正常情况下,输出错误或丢包应该为0。 ethtool查看和设置网卡振铃内存。 ethtool -g 可以显示某个网卡的环形缓冲区,如下面的例子 ![]()
ethtool -G eth0 rx 8192 设置其值。 Linux 丢包
UDP报文错误
![]()
防火墙
UDP缓冲区大小不足
sysctl -w net.core.rmem_max=26214400 # 设置为 25Mnetdev_max_backlog,它指定 Linux 内核从网卡驱动程序读取消息后可以缓存的消息数量。默认值为 1000。您可以增大该值,例如设置成 2000: sudo sysctl -w net.core.netdev_max_backlog=2000系统负载过高
应用丢包
丢包的地方
dropwatch 一个监控丢失信息的工具打印系统中的数据包,并打印丢包的函数地址: ![]()
kfree_skb(网络丢包时会调用该函数) 事件发生: ![]()
总结
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
code前端网