linux系统UDP丢包问题分析思路[转]

linux系统UDP丢包问题分析思路[转]

2023年7月15日发(作者:)

linux系统UDP丢包问题分析思路[转]==========================================================================================

最近⼯作中遇到某个服务器应⽤程序 UDP 丢包,在排查过程中查阅了很多资料,总结出来这篇⽂章,供更多⼈参考。在开始之前,我们先⽤⼀张图解释 linux 系统接收⽹络报⽂的过程。1. ⾸先⽹络报⽂通过物理⽹线发送到⽹卡2. ⽹络驱动程序会把⽹络中的报⽂读出来放到 ring buffer 中,这个过程使⽤ DMA(Direct Memory Access),不需要 CPU 参与3. 内核从 ring buffer 中读取报⽂进⾏处理,执⾏ IP 和 TCP/UDP 层的逻辑,最后把报⽂放到应⽤程序的 socket buffer 中4. 应⽤程序从 socket buffer 中读取报⽂进⾏处理  在接收 UDP 报⽂的过程中,图中任何⼀个过程都可能会主动或者被动地把报⽂丢弃,因此丢包可能发⽣在⽹卡和驱动,也可能发⽣在系统和应⽤。之所以没有分析发送数据流程,⼀是因为发送流程和接收类似,只是⽅向相反;另外发送流程报⽂丢失的概率⽐接收⼩,只有在应⽤程序发送的报⽂速率⼤于内核和⽹卡处理速率时才会发⽣。本篇⽂章假定机器只有⼀个名字为

eth0 的 interface,如果有多个 interface 或者 interface 的名字不是 eth0,请按照实际情况进⾏分析。NOTE:⽂中出现的

RX(receive) 表⽰接收报⽂,TX(transmit) 表⽰发送报⽂。

确认有 UDP 丢包发⽣要查看⽹卡是否有丢包,可以使⽤

ethtool -S eth0 查看,在输出中查找

bad 或者

drop 对应的字段是否有数据,在正常情况下,这些字段对应的数字应该都是 0。如果看到对应的数字在不断增长,就说明⽹卡有丢包。另外⼀个查看⽹卡丢包数据的命令是

ifconfig,它的输出中会有

RX(receive 接收报⽂)和

TX(transmit 发送报⽂)的统计数据:~# RX packets 3553389376 bytes 2599862532475 (2.3 TiB) RX errors 0 dropped 1353 overruns 0 frame 0 TX packets 3479495131 bytes 32 (2.9 TiB) TX errors 0 dropped 0 overruns 0 carrier 0 此外,linux 系统也提供了各个⽹络协议的丢包信息,可以使⽤

netstat -s 命令查看,加上

--udp 可以只看 UDP 相关的报⽂数据:[root@holodesk02 GOD]# netstat -s -uIcmpMsg: InType0: 3 InType3: 1719356 InType8: 13 InType11: 59 OutType0: 13 OutType3: 1737641 OutType8: 10 OutType11: 263Udp: 517488890 packets received 2487375 packets to unknown port received. 47533568 packet receive errors 147264581 packets sent 12851135 receive buffer errors 0 send buffer errorsUdpLite:IpExt: OutMcastPkts: 696 InBcastPkts: 2373968 InOctets: 4954097451540 OutOctets: 5538322535160 OutMcastOctets: 79632 InBcastOctets: 934783053 InNoECTPkts: 5584838675对于上⾯的输出,关注下⾯的信息来查看 UDP 丢包的情况:packet receive errors 不为空,并且在⼀直增长说明系统有 UDP 丢包packets to unknown port received 表⽰系统接收到的 UDP 报⽂所在的⽬标端⼝没有应⽤在监听,⼀般是服务没有启动导致的,并不会造成严重的问题receive buffer errors 表⽰因为 UDP 的接收缓存太⼩导致丢包的数量NOTE: 并不是丢包数量不为零就有问题,对于 UDP 来说,如果有少量的丢包很可能是预期的⾏为,⽐如丢包率(丢包数量/接收报⽂数量)在万分之⼀甚⾄更低。

⽹卡或者驱动丢包之前讲过,如果

ethtool -S eth0 中有

rx_***_errors 那么很可能是⽹卡有问题,导致系统丢包,需要联系服务器或者⽹卡供应商进⾏处理。# ethtool -S eth0 | grep rx_ | grep errors rx_crc_errors: 0 rx_missed_errors: 0 rx_long_length_errors: 0 rx_short_length_errors: 0 rx_align_errors: 0 rx_errors: 0 rx_length_errors: 0 rx_over_errors: 0 rx_frame_errors: 0 rx_fifo_errors: 0netstat -i 也会提供每个⽹卡的接发报⽂以及丢包的情况,正常情况下输出中 error 或者 drop 应该为 0。如果硬件或者驱动没有问题,⼀般⽹卡丢包是因为设置的缓存区(ring buffer)太⼩,可以使⽤

ethtool 命令查看和设置⽹卡的 ring buffer。ethtool -g 可以查看某个⽹卡的 ring buffer,⽐如下⾯的例⼦# ethtool -g eth0Ring parameters for eth0:Pre-set maximums:RX: 4096RX Mini: 0RX Jumbo: 0TX: 4096Current hardware settings:RX: 256RX Mini: 0RX Jumbo: 0TX: 256Pre-set 表⽰⽹卡最⼤的 ring buffer 值,可以使⽤

ethtool -G eth0 rx 8192 设置它的值。

Linux 系统丢包linux 系统丢包的原因很多,常见的有:UDP 报⽂错误、防⽕墙、UDP buffer size 不⾜、系统负载过⾼等,这⾥对这些丢包原因进⾏分析。UDP 报⽂错误如果在传输过程中UDP 报⽂被修改,会导致 checksum 错误,或者长度错误,linux 在接收到 UDP 报⽂时会对此进⾏校验,⼀旦发明错误会把报⽂丢弃。如果希望 UDP 报⽂ checksum 及时有错也要发送给应⽤程序,可以在通过 socket 参数禁⽤ UDP checksum 检查:int disable = 1;setsockopt(sock_fd, SOL_SOCKET, SO_NO_CHECK, (void*)&disable, sizeof(disable)防⽕墙如果系统防⽕墙丢包,表现的⾏为⼀般是所有的 UDP 报⽂都⽆法正常接收,当然不排除防⽕墙只 drop ⼀部分报⽂的可能性。如果遇到丢包⽐率⾮常⼤的情况,请先检查防⽕墙规则,保证防⽕墙没有主动 drop UDP 报⽂。UDP buffer size 不⾜(我就是在这⼀步排查,解决掉问题的)linux 系统在接收报⽂之后,会把报⽂保存到缓存区中。因为缓存区的⼤⼩是有限的,如果出现 UDP 报⽂过⼤(超过缓存区⼤⼩或者 MTU⼤⼩)、接收到报⽂的速率太快,都可能导致 linux 因为缓存满⽽直接丢包的情况。在系统层⾯,linux 设置了 receive buffer 可以配置的最⼤值,可以在下⾯的⽂件中查看,⼀般是 linux 在启动的时候会根据内存⼤⼩设置⼀个初始值。/proc/sys/net/core/rmem_max:允许设置的 receive buffer 最⼤值/proc/sys/net/core/rmem_default:默认使⽤的 receive buffer 值/proc/sys/net/core/wmem_max:允许设置的 send buffer 最⼤值/proc/sys/net/core/wmem_default:默认使⽤的 send buffer 最⼤值但是这些初始值并不是为了应对⼤流量的 UDP 报⽂,如果应⽤程序接收和发送 UDP 报⽂⾮常多,需要讲这个值调⼤。可以使⽤

sysctl 命令让它⽴即⽣效(注意:使⽤该命令⽣效后,重启后会恢复原来值):sysctl -w _max=26214400 # 设置为 25M也可以修改

/etc/ 中对应的参数在下次启动时让参数保持⽣效。如果报⽂报⽂过⼤,可以在发送⽅对数据进⾏分割,保证每个报⽂的⼤⼩在 MTU 内。另外⼀个可以配置的参数是

netdev_max_backlog,它表⽰ linux 内核从⽹卡驱动中读取报⽂后可以缓存的报⽂数量,默认是 1000,可以调⼤这个值,⽐如设置成 2000:sudo sysctl -w _max_backlog=2000系统负载过⾼系统 CPU、memory、IO 负载过⾼都有可能导致⽹络丢包,⽐如 CPU 如果负载过⾼,系统没有时间进⾏报⽂的 checksum 计算、复制内存等操作,从⽽导致⽹卡或者 socket buffer 出丢包;memory 负载过⾼,会应⽤程序处理过慢,⽆法及时处理报⽂;IO 负载过⾼,CPU 都⽤来响应 IO wait,没有时间处理缓存中的 UDP 报⽂。linux 系统本⾝就是相互关联的系统,任何⼀个组件出现问题都有可能影响到其他组件的正常运⾏。对于系统负载过⾼,要么是应⽤程序有问题,要么是系统不⾜。对于前者需要及时发现,debug 和修复;对于后者,也要及时发现并扩容。

应⽤丢包上⾯提到系统的 UDP buffer size,调节的 sysctl 参数只是系统允许的最⼤值,每个应⽤程序在创建 socket 时需要设置⾃⼰ socket buffersize 的值。linux 系统会把接受到的报⽂放到 socket 的 buffer 中,应⽤程序从 buffer 中不断地读取报⽂。所以这⾥有两个和应⽤有关的因素会影响是否会丢包:socket buffer size ⼤⼩以及应⽤程序读取报⽂的速度。对于第⼀个问题,可以在应⽤程序初始化 socket 的时候设置 socket receive buffer 的⼤⼩,⽐如下⾯的代码把 socket buffer 设置为 20MB:uint64_t receive_buf_size = 20*1024*1024; //20 MBsetsockopt(socket_fd, SOL_SOCKET, SO_RCVBUF, &receive_buf_size, sizeof(receive_buf_size));如果不是⾃⼰编写和维护的程序,修改应⽤代码是件不好甚⾄不太可能的事情。很多应⽤程序会提供配置参数来调节这个值,请参考对应的官⽅⽂档;如果没有可⽤的配置参数,只能给程序的开发者提 issue 了。很明显,增加应⽤的 receive buffer 会减少丢包的可能性,但同时会导致应⽤使⽤更多的内存,所以需要谨慎使⽤。另外⼀个因素是应⽤读取 buffer 中报⽂的速度,对于应⽤程序来说,处理报⽂应该采取异步的⽅式

包丢在什么地⽅想要详细了解 linux 系统在执⾏哪个函数时丢包的话,可以使⽤

dropwatch ⼯具,它监听系统丢包信息,并打印出丢包发⽣的函数地址:# dropwatch -l kasInitalizing kallsyms dbdropwatch> Kernel monitoring Ctrl-C to stop monitoring1 drops at tcp_v4_do_rcv+cd (0xffffffff81799bad)10 drops at tcp_v4_rcv+80 (0xffffffff8179a620)1 drops at sk_stream_kill_queues+57 (0xffffffff81729ca7)4 drops at unix_release_sock+20e (0xffffffff817dc94e)1 drops at igmp_rcv+e1 (0xffffffff817b4c41)1 drops at igmp_rcv+e1 (0xffffffff817b4c41)通过这些信息,找到对应的内核代码处,就能知道内核在哪个步骤中把报⽂丢弃,以及⼤致的丢包原因。此外,还可以使⽤ linux perf ⼯具监听

kfree_skb(把⽹络报⽂丢弃时会调⽤该函数) 事件的发⽣:sudo perf record -g -a -e skb:kfree_skbsudo perf script关于 perf 命令的使⽤和解读,⽹上有很多⽂章可以参考。

总结UDP 本⾝就是⽆连接不可靠的协议,适⽤于报⽂偶尔丢失也不影响程序状态的场景,⽐如视频、⾳频、游戏、监控等。对报⽂可靠性要求⽐较⾼的应⽤不要使⽤ UDP,推荐直接使⽤ TCP。当然,也可以在应⽤层做重试、去重保证可靠性如果发现服务器丢包,⾸先通过监控查看系统负载是否过⾼,先想办法把负载降低再看丢包问题是否消失如果系统负载过⾼,UDP 丢包是没有有效解决⽅案的。如果是应⽤异常导致 CPU、memory、IO 过⾼,请及时定位异常应⽤并修复;如果是资源不够,监控应该能及时发现并快速扩容对于系统⼤量接收或者发送 UDP 报⽂的,可以通过调节系统和程序的 socket buffer size 来降低丢包的概率应⽤程序在处理 UDP 报⽂时,要采⽤异步⽅式,在两次接收报⽂之间不要有太多的处理逻辑

参考资料

发布者:admin,转转请注明出处:http://www.yc00.com/xiaochengxu/1689409876a243490.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信