linux查看udp丢包数量,Linux下UDP丢包问题分析思路

最近工作中需要为PHP服务提供存储prometheus指标数据的UDP服务器。然后就用Netty实现了一个简单地UDP服务。但是在压测的过程中发现有严重的丢包现象。下面我们就来分析一下丢包发生的原因以及解决办法

1.linux 系统接收网络报文的过程

接收数据包是一个复杂的过程,涉及很多底层的技术细节,但大致需要以下几个步骤:

网卡收到数据包。

将数据包从网卡硬件缓存转移到服务器内存中。

通知内核处理。

经过TCP/IP协议逐层处理。

应用程序通过read()从socket buffer读取数据。

22b0f89937ef?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

网络报文接受过程

在接收 UDP 报文的过程中,图中任何一个过程都可能会主动或者被动地把报文丢弃,因此丢包可能发生在网卡和驱动,也可能发生在系统和应用。

如想要详细的了解这个过程可以参考美团的一篇文章:Redis 高负载下的中断优化

2.确认UDP丢包

排查丢包问题的第一步就是要确认UDP丢包了。一般有以下两个途径:

从UDP服务所产生的业务信息中去核对

从Linux服务统计信息中查看

那么我们首先看第一种方式,也是比较简单和直观的方式。

2.1.从UDP服务所产生的业务信息中去核对

比如,我们的UDP服务器的功能就是提供存储prometheus的指标数据的功能。那么我们就可以通过Count类型的指标数据来衡量是否丢包了。其原理也比较简单,如下图

22b0f89937ef?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

原理图

client发起UDP请求,UDP服务器就+1,然后测试结束后通过Http服务,把数据暴露给用户。暴露数据给用户的方式可以有多种,比如可以写日志。这个根据业务场景来。由于我们本来就是要暴露数据给prometheus服务器的,所以直接Http请求就可以抓取到数据了。

我们实际场景下的测试数据如下:

22b0f89937ef?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

Http暴露的数据

22b0f89937ef?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

测试用例

可以看到两者数据相等,那么就说明没有丢包。

2.2 从Linux服务统计信息中查看

确认网卡和驱动是否丢包

要查看网卡是否有丢包,可以使用 ethtool -S eth0 查看,在输出中查找 bad 或者 drop 对应的字段是否有数据,在正常情况下,这些字段对应的数字应该都是 0。如果看到对应的数字在不断增长,就说明网卡有丢包。

# 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: 0

另外一个查看网卡丢包数据的命令是 ifconfig,它的输出中会有 RX(receive 接收报文)和 TX(transmit 发送报文)的统计数据:

~# ifconfig eth0

...

RX packets 3553389376 bytes 2599862532475 (2.3 TiB)

RX errors 0 dropped 1353 overruns 0 frame 0

TX packets 3479495131 bytes 3205366800850 (2.9 TiB)

TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

...

如果硬件或者驱动没有问题,一般网卡丢包是因为设置的缓存区(ring buffer)太小,可以使用 ethtool 命令查看和设置网卡的 ring buffer。

ethtool -g 可以查看某个网卡的 ring buffer,比如下面的例子

# ethtool -g eth0

Ring parameters for eth0:

Pre-set maximums:

RX: 4096

RX Mini: 0

RX Jumbo: 0

TX: 4096

Current hardware settings:

RX: 256

RX Mini: 0

RX Jumbo: 0

TX: 256

Pre-set 表示网卡最大的 ring buffer 值,可以使用 ethtool -G eth0 rx 8192 设置它的值。

Linux系统是否丢弃相关网络协议的包

可以使用 netstat -s 命令查看,加上 --udp 可以只看 UDP 相关的报文数据:

[root@holodesk02 GOD]# netstat -s -u

IcmpMsg:

InType0: 3

InType3: 1719356

InType8: 13

InType11: 59

OutType0: 13

OutType3: 1737641

OutType8: 10

OutType11: 263

Udp:

517488890 packets received

2487375 packets to unknown port received.

47533568 packet receive errors

147264581 packets sent

12851135 receive buffer errors

0 send buffer errors

UdpLite:

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 来说,如果有少量的丢包很可能是预期的行为,比如丢包率(丢包数量/接收报文数量)在万分之一甚至更低。

3.UDP丢包原因分析

Linux 系统丢包的原因很多,常见的有:UDP 报文错误、防火墙、UDP buffer size 不足、系统负载过高等,这里对这些丢包原因进行分析。

3.1防火墙

如果系统是因为防火墙而丢包,表现的行为一般是所有的 UDP 报文都无法正常接收,当然不排除防火墙只 drop 一部分报文的可能性。

如果遇到丢包比率非常大的情况,请先检查防火墙规则,保证防火墙没有主动 drop UDP 报文。

3.2. 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_dafault:默认使用的 send buffer 最大值

但是这些初始值并不是为了应对大流量的 UDP 报文,如果应用程序接收和发送 UDP 报文非常多,需要讲这个值调大。可以使用 sysctl 命令让它立即生效:

sysctl -w net.core.rmem_max=26214400 # 设置为 25M

也可以修改 /etc/sysctl.conf 中对应的参数在下次启动时让参数保持生效。

注意,如果是用Jmeter在Linux上压测的时候,那么Jmeter所在的机器需要调大写缓冲区net.core.wmem_max

如果报文报文过大,可以在发送方对数据进行分割,保证每个报文的大小在 MTU 内。

另外一个可以配置的参数是 netdev_max_backlog,它表示 linux 内核从网卡驱动中读取报文后可以缓存的报文数量,默认是 1000,可以调大这个值,比如设置成 2000:

sudo sysctl -w net.core.netdev_max_backlog=2000

3.3. 系统负载过高

系统 CPU、memory、IO 负载过高都有可能导致网络丢包,比如 CPU 如果负载过高,系统没有时间进行报文的 checksum 计算、复制内存等操作,从而导致网卡或者 socket buffer 出丢包;memory 负载过高,会应用程序处理过慢,无法及时处理报文;IO 负载过高,CPU 都用来响应 IO wait,没有时间处理缓存中的 UDP 报文。

linux 系统本身就是相互关联的系统,任何一个组件出现问题都有可能影响到其他组件的正常运行。对于系统负载过高,要么是应用程序有问题,要么是系统不足。对于前者需要及时发现,debug 和修复;对于后者,也要及时发现并扩容。

3.4. 应用丢包

上面提到系统的 UDP buffer size,调节的 sysctl 参数只是系统允许的最大值,每个应用程序在创建 socket 时需要设置自己 socket buffer size 的值。

linux 系统会把接受到的报文放到 socket 的 buffer 中,应用程序从 buffer 中不断地读取报文。所以这里有两个和应用有关的因素会影响是否会丢包:socket buffer size 大小以及应用程序读取报文的速度。

在Netty中可以这样设置UDP缓冲区的大小

bootstrap.group(group)

.channel(NioDatagramChannel.class )

.option(ChannelOption.SO_BROADCAST, true)

.option(ChannelOption.SO_RCVBUF, 1024 * 1024 )//设置缓冲区为1M,如果不够可以再调大

.handler( new ChannelInitializer() {

@Override

protected void initChannel(Channel channel)

throws Exception {

ChannelPipeline pipeline = channel.pipeline();

//...

}

});

很明显,增加应用的 receive buffer 会减少丢包的可能性,但同时会导致应用使用更多的内存,所以需要谨慎使用。

另外一个因素是应用读取 buffer 中报文的速度,对于应用程序来说,处理报文应该采取异步的方式

4.总结

UDP 本身就是无连接不可靠的协议,适用于报文偶尔丢失也不影响程序状态的场景,比如视频、音频、游戏、监控等。对报文可靠性要求比较高的应用不要使用 UDP,推荐直接使用 TCP。当然,也可以在应用层做重试、去重保证可靠性

如果发现服务器丢包,首先通过监控查看系统负载是否过高,先想办法把负载降低再看丢包问题是否消失

如果系统负载过高,UDP 丢包是没有有效解决方案的。如果是应用异常导致 CPU、memory、IO 过高,请及时定位异常应用并修复;如果是资源不够,监控应该能及时发现并快速扩容

对于系统大量接收或者发送 UDP 报文的,可以通过调节系统和程序的 socket buffer size 来降低丢包的概率

应用程序在处理 UDP 报文时,要采用异步方式,在两次接收报文之间不要有太多的处理逻辑

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值