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

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

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

网卡收到数据包。

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

通知内核处理。

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

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

网络报文接受过程

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

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

2.确认UDP丢包

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

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

从Linux服务统计信息中查看

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

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

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

原理图

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

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

Http暴露的数据

测试用例

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

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 报文时,要采用异步方式,在两次接收报文之间不要有太多的处理逻辑

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

  1. Linux查看WAS的jvm信息,linux 下使用命令查看jvm信息

    java程序员除了编写业务代码之外,特别是项目上线之后,更需要关注的是系统的性能表现,这个时候就需要了解一下jvm的性能表现了,可以借助于java虚拟机自带的一些分析工具,主要有三个常用的命令. 1. ...

  2. linux查看某进程的连接,linux下查看指定进程的所有连接信息(转)

    定位某个进程的网络故障时经常需要用到的一个功能就是查找所有连接的信息.通常查找某个端口的连接信息使用 ss 或者 netstat 可以轻松拿到,如果是主动与别的机器建立的连接信息则可以通过 lsof ...

  3. linux查看mysql连接日志文件,linux下查看mysql日志文件的方法

    查看mysql日志方法: mysql默认不允许我们查看日志.需要更改一些设置 1 vi 更改配置文件 允许用户查看日志文件 sudo vi /etc/mysql/mysql.conf.d/mysqld ...

  4. linux 查看磁盘空间 拷贝不同目录下的文件

    linux 查看磁盘空间 df -h 拷贝不同目录下的文件 cp -rf 目录1 目录2 目录1是需要拷贝的文件所在目录 目录2是需要存放的位置 cp -rf /data/ws/m2e2/ ././表 ...

  5. linux 查看磁盘信息命令行,linux命令-查看硬盘信息(磁盘阵列模式下)

    如果服务器有做磁盘阵列,那么运行fdisk -l或者smartctl –all /dev/sda,得不到你要的磁盘信息. [root@localhost /]# fdisk -l WARNING: G ...

  6. linux查看主板最大内存容量,Linux 查看内存插槽数、最大容量的方法

    Linux 查看内存插槽数.最大容量的方法 查看内存插槽数: dmidecode|grep -P -A5 "Memory\s+Device"|grep Size|grep -v R ...

  7. linux查看进程运行日志文件,【Linux】常用指令、ps查看进程、kill杀进程、启动停止tomcat命令、查看日志、查看端口、find查找文件...

    1.说出 10 个 linux 常用的指令 1) ls 查看目录中的文件 2)cd /home 进入 '/ home' 目录:cd .. 返回上一级目录:cd ../.. 返回上两级目录 3)mkdi ...

  8. linux查看域名解析地址,windows、linux查看域名解析、跟踪路由

    前言 在介绍域名解析命令前,须要首先理解域名的解析过程,理解解析过程后,能更深入的理解nslookup.dig命令.linux 域名解析 为何要有域名? 假如没有域名,那么访问一个网址,好比百度,须要 ...

  9. linux查看不了防火墙设置,linux怎么样去查看防火墙设置

    我有时候想查看下linux防火墙的设置怎么样,该怎么样查看呢?下面由学习啦小编给你做出详细的linux查看防火墙设置方法介绍!希望对你有帮助! linux查看防火墙设置方法一: 可以使用 servic ...

  10. Linux查看PHP的端口号,linux如何查看端口状态

    linux查看端口状态使用netstat命令. netstat命令各个参数说明如下: -a:列出所有网络状态,包括 Socket 程序: -c秒数:指定每隔几秒刷新一次网络状态: -n:使用 IP 地 ...

最新文章

  1. PTA(BasicLevel)-1007素数对猜想
  2. Mac OS X工具:版本控制SVN
  3. c++ STL 全排列
  4. redis重启命令_请收下这份redis持久化详解
  5. 字符串Ascll格式转16进制
  6. IDEA工具开发必备设置-极大提高开发效率
  7. Linux下命令行压缩照片或图片的脚本 (ImageMagick使用心得,convert,import,display实例)...
  8. pytorch张量操作基础
  9. python种颜色循环_python – 重置Matplotlib中的颜色循环
  10. Linux下Tomcat指定JDK和设置内存大小
  11. Python网络编程(2)-粘包现象及socketserver模块实现TCP并发
  12. Java实现图片上传
  13. 2022新和平精英画质助手iApp源码+附成品/可用的
  14. stdafx.h头文件代码
  15. 风云第三部 第533回 乌云蔽日 力掌乾坤
  16. Golang:后台管理系统Revel搭建教程
  17. python自动化配置路由器_使用Python管理小米路由器
  18. 函数计算机使用说明,SHARP-EL506P 函数计算器的使用说明
  19. 未来两周目标计划---C++ and Disassembly(不积跬步无以至千里,不积小流无以成江海)
  20. 新手如何看k线(图) .

热门文章

  1. 如何将webp格式转换成png?
  2. LFS8.0完全安装搭建制作教程
  3. python 残差图_利用pyFOAM残差的输出
  4. jdk和tomcat的关系
  5. 课程体系包括哪些要素_课程建设包括哪些内容_以课程建设为抓手,促进专业团队的发展...
  6. 科学究研明表,汉字序顺并不一定影阅响读
  7. word转html制作操作手册,Word文档转换为HTML帮助文档操作手册范本.pdf
  8. 电桥测量电路Multisim仿真AD电路原理图PCB
  9. 软件体系结构风格---基于事件的隐式调用
  10. java springboot activemq 邮件短信微服务,解决国际化服务的国内外兼容性问题,含各服务商调研情况...