toc
问题: 两台服务器之间使用UDP通信,服务器A确认消息已发出,服务器B没有收到消息,请问有哪些可能?
1. 导读
这个问题如果直接去处理,可能会考虑框架日志、clb日志、k8s网卡日志等,反而把问题弄复杂了。其实可以理解为“丢包”问题,UDP丢包是非常常见的问题,由于协议本身就是非链接的传输协议,是不可靠的;所以准备从UDP协议原理出发,探讨下丢包的各种可能。
2. 什么是UDP?
原理可以参考计算机网络(第7版)谢希仁.pdf
UDP是User Datagram Protocol的简称,中文名是用户数据报协议,是OSI参考模型中的传输层协议,它是一种**无连接的传输层协议**,提供面向事务的简单**不可靠**信息传送服务。
3. 导致丢包的原因?
3.1 UDP socket缓冲区满造成的UDP丢包
- 原因 如果 socke t缓冲区满了,应用程序没来得及处理在缓冲区中的 UDP 包,那么后续来的 UDP 包会被内核丢弃,造成丢包
- 解决思路
在 socket 缓冲区满造成丢包的情况下,可以通过
增大缓冲区
的方法来缓解UDP丢包问题。注意
:如果服务已经过载
了,简单的增大缓冲区并不能解决问题,反而会造成滚雪球效应,造成请求全部超时
,服务不可用。
3.2 UDP socket缓冲区过小或数据过大造成的UDP丢包
- 原因 如果Client 发送的 UDP 报文很大,而 socket 缓冲区过小无法容下该 UDP 报文,那么该报文就会丢失。
- 解决思路 可以适当增大socket缓冲区。
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 接收端处理时间过长导致丢包
- 原因
调用 recv 方法接收端收到数据后,处理数据花了一些时间,处理完后再次调用 recv 方法,在这
二次调用间隔
里,发过来的包可能丢失。 - 解决思路
对于这种情况可以修改接收端,数据接收与处理应当分离,将包接收后存入一个缓冲区,然后迅速返回继续 recv。
3.5 发送的包巨大丢包
- 原因 虽然 send 方法会帮你做大包切割成小包发送的事情,但包太大也不行。
例如超过 50K 的一个 udp 包,不切割直接通过send 方法发送也会导致这个包丢失。
- 解决思路
这种情况发送端需要切割成小包再逐个 send。以前在遇到过类似问题,日志上报常见,超长日志导致丢失。
3.6 发送的包频率太快
- 原因 虽然每个包的大小都小于 mtu size 但是频率太快,例如 40 多个 mut size 的包连续发送中间不 sleep,可能会导致3.1中缓冲区满的问题。
由于UDP没有确认机制,没有流量控制和拥塞控制,这样在网络出现拥塞或通信两端处理能力不匹配的时候,UDP并不会进行调整发送速率,从而导致大量丢包。
- 解决思路
发送端可以适当加入频率限制,适当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 观察接收服务器负载是否过高?
观察是否出现以下情况,如果是,可以先扩容,然后进行观察。
- 内存占用过高
- CPU过高
- 磁盘IO太忙
- 磁盘满了
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 高效吗?