UDP 请求丢失有哪些原因?

2023-02-27 19:03:50 浏览数 (1)

toc

问题: 两台服务器之间使用UDP通信,服务器A确认消息已发出,服务器B没有收到消息,请问有哪些可能?

1. 导读

这个问题如果直接去处理,可能会考虑框架日志、clb日志、k8s网卡日志等,反而把问题弄复杂了。其实可以理解为“丢包”问题,UDP丢包是非常常见的问题,由于协议本身就是非链接的传输协议,是不可靠的;所以准备从UDP协议原理出发,探讨下丢包的各种可能。

2. 什么是UDP?

原理可以参考计算机网络(第7版)谢希仁.pdf

UDP是User Datagram Protocol的简称,中文名是用户数据报协议,是OSI参考模型中的传输层协议,它是一种**无连接的传输层协议**,提供面向事务的简单**不可靠**信息传送服务。

3. 导致丢包的原因?

3.1 UDP socket缓冲区满造成的UDP丢包

  1. 原因 如果 socke t缓冲区满了,应用程序没来得及处理在缓冲区中的 UDP 包,那么后续来的 UDP 包会被内核丢弃,造成丢包
  2. 解决思路 在 socket 缓冲区满造成丢包的情况下,可以通过增大缓冲区的方法来缓解UDP丢包问题。 注意:如果服务已经过载了,简单的增大缓冲区并不能解决问题,反而会造成滚雪球效应,造成请求全部超时,服务不可用。

3.2 UDP socket缓冲区过小或数据过大造成的UDP丢包

  1. 原因 如果Client 发送的 UDP 报文很大,而 socket 缓冲区过小无法容下该 UDP 报文,那么该报文就会丢失。
  2. 解决思路 可以适当增大socket缓冲区。
代码语言:txt复制
int nReint nRecvBuf=32*1024;//设置为32K

setsockopt(s,SOL_SOCKET,SO_RCVBUF,(constchar*)&nRecvBuf,sizeof(int));

3.3 ARP缓存过期导致UDP丢包

数据链路层地址解析协议APR(Address Resolution Protocol), 是根据IP地址获取物理地址的一个TCP/IP协议。主机发送信息时将包含目标IP地址的ARP请求广播到局域网络上的所有主机,并接收返回消息,以此确定目标的物理地址;收到返回消息后将该IP地址和物理地址存入本机ARP缓存中并保留一定时间,下次请求时直接查询ARP缓存以节约资源。

ARP 的缓存时间约 10 分钟,APR 缓存列表没有对方的 MAC 地址或缓存过期的时候,会发送 ARP 请求获取 MAC 地址,在没有获取到 MAC 地址之前,用户发送出去的 UDP 数据包会被内核缓存到 arp_queue 这个队列中,默认最多缓存 3 个包,多余的 UDP 包会被丢弃。

3.4 接收端处理时间过长导致丢包

  1. 原因 调用 recv 方法接收端收到数据后,处理数据花了一些时间,处理完后再次调用 recv 方法,在这二次调用间隔里,发过来的包可能丢失。
  2. 解决思路

对于这种情况可以修改接收端,数据接收与处理应当分离,将包接收后存入一个缓冲区,然后迅速返回继续 recv。

3.5 发送的包巨大丢包

  1. 原因 虽然 send 方法会帮你做大包切割成小包发送的事情,但包太大也不行。

例如超过 50K 的一个 udp 包,不切割直接通过send 方法发送也会导致这个包丢失。

  1. 解决思路

这种情况发送端需要切割成小包再逐个 send。以前在遇到过类似问题,日志上报常见,超长日志导致丢失。

3.6 发送的包频率太快

  1. 原因 虽然每个包的大小都小于 mtu size 但是频率太快,例如 40 多个 mut size 的包连续发送中间不 sleep,可能会导致3.1中缓冲区满的问题。

由于UDP没有确认机制,没有流量控制和拥塞控制,这样在网络出现拥塞或通信两端处理能力不匹配的时候,UDP并不会进行调整发送速率,从而导致大量丢包。

  1. 解决思路

发送端可以适当加入频率限制,适当sleep。

4. 遇到丢包,如何排查?

4.1. UDP 丢包检测方法

使用netstat命令,加-su参数。

代码语言:txt复制
bash-4.2# netstat -su

IcmpMsg:

    InType3: 3

    InType8: 2172282

    InType11: 1

    OutType0: 2172282

Udp:

    2272520 packets received

    0 packets to unknown port received.

    0 packet receive errors

    2272520 packets sent

    `0 receive buffer errors`

    0 send buffer errors

UdpLite:

IpExt:

    InMcastPkts: 46285

    InOctets: 8932526993

    OutOctets: 7243339494

    InMcastOctets: 1666260

    InNoECTPkts: 54319536

每隔一段时间执行此命令,观察packet receive errors,如果不断变大则发生了丢包。

4.2 观察接收服务器负载是否过高?

观察是否出现以下情况,如果是,可以先扩容,然后进行观察。

  1. 内存占用过高
  2. CPU过高
  3. 磁盘IO太忙
  4. 磁盘满了

4.3 查看socket缓冲区的设置是否合理

通过 cat /proc/sys/net/core/rmem_default 和cat /proc/sys/net/core/rmem_max可以查看socket缓冲区的缺省值和最大值。如果服务器的性能压力不大,对处理时延也没有很严格的要求,设置为1M左右即可。如果服务器的性能压力较大,或者对处理时延有很严格的要求,则必须谨慎设置rmem_default 和rmem_max,如果设得过小,会导致丢包,如果设得过大,会出现滚雪球。socket缓冲区设的过大导致滚雪球的问题,大家可以参考http://km.woa.com/user/bisonliao/articles/show/75674中的介绍,有定量的计算方法,分析得很深入。

4.4 观察发送端日志,发送频率是否过高?

UDP本身没有流量控制和拥塞控制,如果某些业务场景存在频繁发送的情况,可以适当增加延时发送逻辑。

5. 其他方案选择?

如果排除了以上的原因,丢包率依旧较高,业务对可靠性要求较高,可以考虑以下方案。

5.1 TCP协议

TCP是面向连接的传输层协议,拥有流量控制、拥塞控制、有序的可靠传输协议。

缺点:相较于UDP,TCP的性能较差,连接成本高,速度慢。

5.2 KCP协议

KCP是一个快速可靠协议,能以比 TCP浪费10%-20%的带宽的代价,换取平均延迟降低 30%-40%,且最大延迟降低三倍的传输效果。纯算法实现,并不负责底层协议(如UDP)的收发,需要使用者自己定义下层数据包的发送方式,以 callback的方式提供给 KCP。 连时钟都需要外部传递进来,内部不会有任何一次系统调用。

5.3 QUIC

QUIC(Quick UDP Internet Connection)是谷歌制定的一种基于UDP的低时延的互联网传输层协议。QUIC很好地解决了当今传输层和应用层面临的各种需求,包括处理更多的连接,安全性,和低延迟。QUIC融合了包括TCP,TLS,HTTP/2等协议的特性,但基于UDP传输。

6. 参考资料

  • 告知你不为人知的UDP-疑难杂症和使用
  • 终于有人能讲清楚了 UDP 分片 与 丢包,UDP 真的比 TCP 高效吗?
udp

0 人点赞