用了TCP协议就一定不会丢包吗( 三 )

增大之后,可以减少因为容量小而导致的丢包情况 。
网卡性能不足
网卡作为硬件,传输速度是有上限的 。当网络传输速度过大,达到网卡上限时,就会发生丢包 。这种情况一般常见于压测场景 。
我们可以通过加网卡名,获得当前网卡支持的最大速度 。
# ethtool eth0Settings for eth0:Speed: 10000Mb/s
可以看到,我这边用的网卡能支持的最大传输速度speed=/s 。
也就是俗称的千兆网卡,但注意这里的单位是Mb,这里的b是指bit,而不是Byte 。1Byte=8bit 。所以/s还要除以8,也就是理论上网卡最大传输速度是1000/8 = 125MB/s 。
我们可以通过sar命令从网络接口层面来分析数据包的收发情况 。
# sar -n DEV 1Linux 3.10.0-1127.19.1.el7.x86_642022年07月27日_x86_64_(1 CPU)08时35分39秒IFACErxpck/stxpck/srxkB/stxkB/srxcmp/stxcmp/srxmcst/s08时35分40秒eth06.064.040.35121682.330.000.000.0
其中 txkB/s是指当前每秒发送的字节(byte)总数,rxkB/s是指每秒接收的字节(byte)总数 。
当两者加起来的值约等于12~13w字节的时候,也就对应大概125MB/s的传输速度 。此时达到网卡性能极限,就会开始丢包 。
遇到这个问题,优先看下你的服务是不是真有这么大的真实流量,如果是的话可以考虑下拆分服务,或者就忍痛充钱升级下配置吧 。
接收缓冲区丢包
我们一般使用TCP 进行网络编程的时候,内核都会分配一个发送缓冲区和一个接收缓冲区 。
当我们想要发一个数据包,会在代码里执行send(msg),这时候数据包并不是一把梭直接就走网卡飞出去的 。而是将数据拷贝到内核发送缓冲区就完事返回了,至于什么时候发数据,发多少数据,这个后续由内核自己做决定 。之前写过的《代码执行send成功后,数据就发出去了吗?》里有比较详细的介绍 。
逻辑
而接收缓冲区作用也类似,从外部网络收到的数据包就暂存在这个地方,然后坐等用户空间的应用程序将数据包取走 。
这两个缓冲区是有大小限制的,可以通过下面的命令去查看 。
# 查看接收缓冲区# sysctl net.ipv4.tcp_rmemnet.ipv4.tcp_rmem = 4096873806291456# 查看发送缓冲区# sysctl net.ipv4.tcp_wmemnet.ipv4.tcp_wmem = 4096163844194304
不管是接收缓冲区还是发送缓冲区,都能看到三个数值,分别对应缓冲区的最小值,默认值和最大值 (min、、max) 。缓冲区会在min和max之间动态调整 。
那么问题来了,如果缓冲区设置过小会怎么样?
对于发送缓冲区,执行send的时候,如果是阻塞调用,那就会等,等到缓冲区有空位可以发数据 。
send阻塞
如果是非阻塞调用,就会立刻返回一个错误信息,意思是 Try again。让应用程序下次再重试 。这种情况下一般不会发生丢包 。
?
send非阻塞
当接受缓冲区满了,事情就不一样了,它的TCP接收窗口会变为0,也就是所谓的零窗口,并且会通过数据包里的win=0,告诉发送端,"球球了,顶不住了,别发了" 。一般这种情况下,发送端就该停止发消息了,但如果这时候确实还有数据发来,就会发生丢包 。
?
丢包
我们可以通过下面的命令里的查看到有没有发生过这种丢包现象 。
cat /proc/net/netstatTcpExt: SyncookiesSent TCPRcvQDrop SyncookiesFailedTcpExt: 015760116
但是说个伤心的事情,我们一般也看不到这个,因为这个是5.9版本里引入的打点,而我们的服务器用的一般是2.x~3.x左右版本 。你可以通过下面的命令查看下你用的是什么版本的linux内核 。