共计 11830 个字符,预计需要花费 30 分钟才能阅读完成。
始终以来咱们都晓得 socket 的缓存会对 tcp 性能产生影响,也有有数文章通知咱们应该调大 socke 缓存。然而到底调多大?什么时候调?有哪些伎俩调?具体影响到底如何?这些问题仿佛也没有人真正说明确。上面咱们就构建起一个简略的试验环境,在两台虚拟机之间探索一下 Socket 缓存到底如何影响 TCP 的性能?对剖析过程不感兴趣的能够间接看最初的论断。
影响 Socket 缓存的参数
首先,咱们要先来列出 Linux 中能够影响 Socket 缓存的调整参数。在 proc 目录下,它们的门路和对应阐明为:
/proc/sys/net/core/rmem_default
/proc/sys/net/core/rmem_max
/proc/sys/net/core/wmem_default
/proc/sys/net/core/wmem_max
这些文件用来设置所有 socket 的发送和接管缓存大小,所以既影响 TCP,也影响 UDP。
针对 UDP:
这些参数理论的作用跟 SO_RCVBUF 和 SO_SNDBUF 的 socket option 相干。如果咱们不必 setsockopt 去更改创立进去的 socket buffer 长度的话,那么就应用 rmem_default 和 wmem_default 来作为默认的接管和发送的 socket buffer 长度。如果批改这些 socket option 的话,那么他们能够批改的下限是由 rmem_max 和 wmem_max 来限定的。
针对 TCP:
除了以上四个文件的影响外,还包含如下文件:
/proc/sys/net/ipv4/tcp_rmem
/proc/sys/net/ipv4/tcp_wmem
对于 TCP 来说,下面 core 目录下的四个文件的作用成果一样,只是默认值不再是 rmem_default 和 wmem_default,而是由 tcp_rmem 和 tcp_wmem 文件中所显示的第二个值决定。通过 setsockopt 能够调整的最大值仍然由 rmem_max 和 wmem_max 限度。
查看 tcp_rmem 和 tcp_wmem 的文件内容会发现,文件中蕴含三个值:
[root@localhost network_turning]# cat /proc/sys/net/ipv4/tcp_rmem
4096 131072 6291456
[root@localhost network_turning]# cat /proc/sys/net/ipv4/tcp_wmem
4096 16384 4194304
三个值顺次示意:min default max
min:决定 tcp socket buffer 最小长度。
default:决定其默认长度。
max:决定其最大长度。在一个 tcp 链接中,对应的 buffer 长度将在 min 和 max 之间变动。导致变动的次要因素是以后内存压力。如果应用 setsockopt 设置了对应 buffer 长度的话,这个值将被疏忽。相当于敞开了 tcp buffer 的动静调整。
/proc/sys/net/ipv4/tcp_moderate_rcvbuf
这个文件是服务器是否反对缓存动静调整的开关,1 为默认值关上,0 为敞开。
另外要留神的是,应用 setsockopt 设置对应 buffer 长度的时候,理论失效的值将是设置值的 2 倍。
当然,这外面所有的 rmem 都是针对接管缓存的限度,而 wmem 都是针对发送缓存的限度。
咱们目前的试验环境配置都采纳默认值:
[root@localhost network_turning]# cat /proc/sys/net/core/rmem_default
212992
[root@localhost network_turning]# cat /proc/sys/net/core/rmem_max
212992
[root@localhost network_turning]# cat /proc/sys/net/core/wmem_default
212992
[root@localhost network_turning]# cat /proc/sys/net/core/wmem_max
212992
另外须要阐明的是,咱们目前的试验环境是两台虚拟机,一个是 centos 8,另一个是 fedora 31:
[root@localhost network_turning]# uname -r
5.5.15-200.fc31.x86_64
[root@localhost zorro]# uname -r
4.18.0-147.5.1.el8_1.x86_64
咱们将要做的测试也很简略,咱们将在 centos 8 上开启一个 web 服务,并共享一个 bigfile。而后在 fedora 31 下来下载这个文件。通过下载的速度来察看 socket 缓存对 tcp 的性能影响。咱们先来做一下基准测试,以后在默认设置下,下载速度为:
[root@localhost zorro]# wget --no-proxy http://192.168.247.129/bigfile
--2020-04-13 14:01:33-- http://192.168.247.129/bigfile
Connecting to 192.168.247.129:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 1073741824 (1.0G)
Saving to: 'bigfile'
bigfile 100%[=====================================>] 1.00G 337MB/s in 3.0s
2020-04-13 14:01:36 (337 MB/s) - 'bigfile' saved [1073741824/1073741824]
bigfile 是个 1G 的文件,在同一个宿主机的两个虚拟机之间,他们的传输速率达到了 337MB/s。这是以后基准环境状态。影响虚拟机之间的带宽的因素较多,咱们心愿在测试过程中尽量避免其余因素烦扰。所以这里咱们打算对 web 服务器的 80 端口进行限速。为了不影响其余过程的速率,咱们应用 htb 进行限速,脚本如下:
[root@localhost zorro]# cat htb.sh
#!/bin/bash
tc qd del dev ens33 root
tc qd add dev ens33 root handle 1: htb default 100
tc cl add dev ens33 parent 1: classid 1:1 htb rate 20000mbit burst 20k
tc cl add dev ens33 parent 1:1 classid 1:10 htb rate 1000mbit burst 20k
tc cl add dev ens33 parent 1:1 classid 1:100 htb rate 20000mbit burst 20k
tc qd add dev ens33 parent 1:10 handle 10: fq_codel
tc qd add dev ens33 parent 1:100 handle 100: fq_codel
tc fi add dev ens33 protocol ip parent 1:0 prio 1 u32 match ip sport 80 0xffff flowid 1:10
应用 htb 给网络流量做了 2 个分类,针对 80 端口的流量限度了 1000mbit/ s 的速率限度,其余端口是 20000mbit/ s 的限度,这在以后环境下相当于没有限速。之后,咱们在 centos 8 的 web 服务器上执行此脚本并在 fedora 31 上测试下载速率:
[root@localhost zorro]# wget --no-proxy http://192.168.247.129/bigfile
--2020-04-13 14:13:38-- http://192.168.247.129/bigfile
Connecting to 192.168.247.129:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 1073741824 (1.0G)
Saving to: 'bigfile'
bigfile 100%[=====================================>] 1.00G 91.6MB/s in 11s
2020-04-13 14:13:49 (91.7 MB/s) - 'bigfile' saved [1073741824/1073741824]
1000mbit 的速率限度根本符合要求。
那么问题来了,此时 socket 缓存在这个 1000mbit 的带宽限度下,对 tcp 的传输性能有什么影响呢?
如果你喜爱折腾的话,你能够在这个环境上别离调大调小客户端和服务端的缓存大小来别离测试一下,你会发现,此时对 socket 的缓存大小做任何调整,仿佛对 tcp 的传输效率都没有什么影响。
所以这里咱们须要先剖析一下,socket 缓存大小到底在什么状况下会对 tcp 性能有影响?
须要 C /C++ Linux 服务器架构师学习材料加群 563998835(材料包含 C /C++,Linux,golang 技术,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg 等),收费分享
缓存对读写性能的影响
这其实是个通用问题:缓存到底在什么状况下会影响读写性能?
答案也很简略:在读写的相干环节之间有较大的性能差距时,缓存会有比拟大的影响。比方,过程要把数据写到硬盘里。因为硬盘写的速度很慢,而内存很快,所以能够先把数据写到内存里,而后利用水平写操作就很快返回,应用程序此时感觉很快写完了。后续这些数据将由内核帮忙利用把数据从内存再写到硬盘里。
无论如何,当写操作产生数据的速度,大于理论要承受数据的速度时,buffer 才有意义。
在咱们以后的测试环境中,数据下载时,web 服务器是数据发送方,客户端是数据接管方,两头通过虚拟机的网络传输。在计算机上,个别原则上讲,读数据的速率要快于写数据的速率。所以此时两个虚拟机之间并没有写速率大于度速率的问题。所以此时,调整 socket 缓存对 tcp 根本不存在性能影响。
那么如何能力让咱们的模型产生影响呢?
答案也很简略,给网络加比拟大的延时就能够了。如果咱们把每个 tcp 包的传输过程当作一次写操作的话,那么网络延时变大将导致写操作的处理速度变长。网络就会成为应用程序写速度的瓶颈。咱们给咱们的 80 端口再退出一个 200ms 的延时:
[root@localhost zorro]# cat htb.sh
#!/bin/bash
tc qd del dev ens33 root
tc qd add dev ens33 root handle 1: htb default 100
tc cl add dev ens33 parent 1: classid 1:1 htb rate 20000mbit burst 20k
tc cl add dev ens33 parent 1:1 classid 1:10 htb rate 1000mbit burst 20k
tc cl add dev ens33 parent 1:1 classid 1:100 htb rate 20000mbit burst 20k
tc qd add dev ens33 parent 1:10 handle 10: netem delay 200ms
tc qd add dev ens33 parent 1:100 handle 100: fq_codel
tc fi add dev ens33 protocol ip parent 1:0 prio 1 u32 match ip sport 80 0xffff flowid 1:10
再次在 web 服务器上执行此脚本,在客户端 fedora 31 上在延时前后应用 httping 测量一下 rtt 工夫:
[root@localhost zorro]# httping 192.168.247.129
PING 192.168.247.129:80 (/):
connected to 192.168.247.129:80 (426 bytes), seq=0 time= 17.37 ms
connected to 192.168.247.129:80 (426 bytes), seq=1 time= 1.22 ms
connected to 192.168.247.129:80 (426 bytes), seq=2 time= 1.25 ms
connected to 192.168.247.129:80 (426 bytes), seq=3 time= 1.47 ms
connected to 192.168.247.129:80 (426 bytes), seq=4 time= 1.55 ms
connected to 192.168.247.129:80 (426 bytes), seq=5 time= 1.35 ms
^CGot signal 2
--- http://192.168.247.129/ ping statistics ---
6 connects, 6 ok, 0.00% failed, time 5480ms
round-trip min/avg/max = 1.2/4.0/17.4 ms
[root@localhost zorro]# httping 192.168.247.129
PING 192.168.247.129:80 (/):
connected to 192.168.247.129:80 (426 bytes), seq=0 time=404.59 ms
connected to 192.168.247.129:80 (426 bytes), seq=1 time=403.72 ms
connected to 192.168.247.129:80 (426 bytes), seq=2 time=404.61 ms
connected to 192.168.247.129:80 (426 bytes), seq=3 time=403.73 ms
connected to 192.168.247.129:80 (426 bytes), seq=4 time=404.16 ms
^CGot signal 2
--- http://192.168.247.129/ ping statistics ---
5 connects, 5 ok, 0.00% failed, time 6334ms
round-trip min/avg/max = 403.7/404.2/404.6 ms
200ms 的网络延时,体现在 http 协定上会有 400ms 的 rtt 工夫。此时,网络的速率会成为传输过程的瓶颈,尽管带宽没有降落,然而咱们测试一下实在下载速度会发现,带宽无奈利用满了:
[root@localhost zorro]# wget --no-proxy http://192.168.247.129/bigfile
--2020-04-13 14:37:28-- http://192.168.247.129/bigfile
Connecting to 192.168.247.129:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 1073741824 (1.0G)
Saving to: 'bigfile'
bigfile 15%[=====>] 162.61M 13.4MB/s eta 87s
下载速率稳固在 13.4MB/s,离 1000mbit/ s 的实在速率还差的很远。此时就体现出了 tcp 在大延时网络上的性能瓶颈了。那么如何解决呢?
大延时网络进步 TCP 带宽利用率
咱们先来剖析一下以后的问题,为什么加大了网络延时会导致 tcp 带宽利用率降落?
因为咱们的带宽是 1000mbit/s,做个换算为字节数是 125mB/s,当然这是理论值。为了运算不便,咱们假设网络带宽就是 100mB/s。在这样的带宽下,假设没有 buffer 影响,网络发送 1m 数据的速度须要 10ms,之后这 1m 数据须要通过网络发送给对端。而后对端返回接管胜利给服务端,服务端接管到写胜利之后了解为此次写操作实现,之后发送下一个 1m。
在以后网络上咱们发现,1m 自身之需 10ms,然而传输 1m 到对端在等对端反会接管胜利的音讯,要至多 400ms。因为网络一个 rtt 工夫就是 400ms。那么在写 1m 之后,咱们至多要等 400ms 之后能力发送下一个 1M。这样的带宽利用率仅为 10ms(数据发送工夫)/400ms(rtt 等待时间) = 2.5%。这是在没有 buffer 影响的状况下,实际上咱们以后环境是有 buffer 的,所以以后的带宽利用率要远远大于没有 buffer 的实践状况。
有了这个实践模型,咱们就大略晓得应该把 buffer 调整为多大了,实际上就是应该让一次写操作的数据把网络延时,导致节约的带宽填满。在延时为 400ms,带宽为 125mB/ s 的网络上,要填满延时期间的节约带宽的字节数该是多少呢?那就是驰名的带宽延时积了。即:带宽 (125mB/s) X 延时 rtt(0.4s) = 50m。
所以,如果一次写能够写满到 50m,发送给对方。那么期待的 400ms 中实践上将不会有带宽未被利用的状况。那么在以后测试环境中,应该调整的就是发送方的 tcp_wmem 缓存大小。根据上述的各个文件的含意,咱们晓得只有把 /proc/sys/net/ipv4/tcp_wmem 文件中的对应值做调整,那么就会无效影响以后服务端的 tcp socekt buffer 长度。咱们来试一下,在 centos 8 上做如下调整:
[root@localhost zorro]# echo 52428800 52428800 52428800 >/proc/sys/net/ipv4/tcp_wmem
[root@localhost zorro]# cat !$
cat /proc/sys/net/ipv4/tcp_wmem
52428800 52428800 52428800
而后在 fedora 31 测试下载速度:
[root@localhost zorro]# wget --no-proxy http://192.168.247.129/bigfile
--2020-04-13 15:08:54-- http://192.168.247.129/bigfile
Connecting to 192.168.247.129:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 1073741824 (1.0G)
Saving to: 'bigfile'
bigfile 21%[=======>] 222.25M 14.9MB/s eta 69s
发现目前下载速率稳固在 15M/ s 左右。尽管有所晋升,然而仍然并没达到真正充分利用带宽的成果。这是为啥呢?实践错了么?
如果咱们对 TCP 了解比拟深刻的话,咱们会晓得,TCP 传输过程中,真正能决定一次写长度的并不间接受 tcp socket wmem 的长度影响,严格来说,是受到 tcp 发送窗口大小的影响。而 tcp 发送窗口大小还要受到接收端的通告窗口来决定。就是说,tcp 发送窗口决定了是不是能填满大延时网络的带宽,而接收端的通告窗口决定了发送窗口有多大。
那么接受方的通告窗口长度是怎么决定的呢?在内核中,应用 tcp_select_window() 办法来决定通告窗口大小。详细分析这个办法,咱们发现,接受方的通告窗口大小会受到接受方本地的 tcp socket rmem 的残余长度影响。就是说,在一个 tcp 链接中,发送窗口受到对端 tcp socket rmem 残余长度影响。
所以,除了调整发送方 wmem 外,还要调整接受方的 rmem。咱们再来试一下,在 fedora 31 上执行:
[root@localhost zorro]# echo 52428800 52428800 52428800 >/proc/sys/net/ipv4/tcp_rmem
[root@localhost zorro]# cat !$
cat /proc/sys/net/ipv4/tcp_rmem
52428800 52428800 52428800
再做下载测试:
[root@localhost zorro]# wget --no-proxy http://192.168.247.129/bigfile
--2020-04-13 15:21:40-- http://192.168.247.129/bigfile
Connecting to 192.168.247.129:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 1073741824 (1.0G)
Saving to: 'bigfile'
bigfile 100%[=====================================>] 1.00G 92.7MB/s in 13s
2020-04-13 15:21:53 (77.8 MB/s) - 'bigfile' saved [1073741824/1073741824]
这时的下载速率才比拟合乎咱们实践中的情况。当然,因为发送窗口大小受到的是“残余”接管缓存大小影响,所以咱们举荐此时应该把 /proc/sys/net/ipv4/tcp_rmem 的大小调的比理论值更大一些。比方大一倍:
[root@localhost zorro]# echo 104857600 104857600 104857600 > /proc/sys/net/ipv4/tcp_rmem
[root@localhost zorro]# cat /proc/sys/net/ipv4/tcp_rmem
104857600 104857600 104857600
[root@localhost zorro]# wget --no-proxy http://192.168.247.129/bigfile
--2020-04-13 15:25:29-- http://192.168.247.129/bigfile
Connecting to 192.168.247.129:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 1073741824 (1.0G)
Saving to: 'bigfile'
bigfile 100%[=====================================>] 1.00G 89.2MB/s in 13s
2020-04-13 15:25:43 (76.9 MB/s) - 'bigfile' saved [1073741824/1073741824]
此时实践上应该取得比方才更现实的下载速率。另外还有一个文件须要留神:
/proc/sys/net/ipv4/tcp_adv_win_scale
这个值用来影响缓存中有多大空间用来寄存 overhead 相干数据,所谓 overhead 数据能够了解为比方 TCP 报头等非业务数据。假如缓存字节数为 bytes,这个值阐明,有 bytes/ 2 的 tcp_adv_win_scale 次方的空间用来寄存 overhead 数据。默认值为 1 示意有 1 / 2 的缓存空间用来放 overhead,此值为二示意 1 / 4 的空间。当 tcp_adv_win_scale <= 0 的时候,overhead 空间运算为:bytes-bytes/2^(-tcp_adv_win_scale)。取值范畴是:[-31, 31]。
能够在下载过程中应用 ss 命令查看 rcv_space 和 rcv_ssthresh 的变动:
[root@localhost zorro]# ss -io state established '(dport = 80 or sport = 80)'
Netid Recv-Q Send-Q Local Address:Port Peer Address:Port Process
tcp 0 0 192.168.247.130:47864 192.168.247.129:http
ts sack cubic wscale:7,11 rto:603 rtt:200.748/75.374 ato:40 mss:1448 pmtu:1500 rcvmss:1448 advmss:1448 cwnd:10 bytes_sent:149 bytes_acked:150 bytes_received:448880 segs_out:107 segs_in:312 data_segs_out:1 data_segs_in:310 send 577.0Kbps lastsnd:1061 lastrcv:49 lastack:50 pacing_rate 1.2Mbps delivery_rate 57.8Kbps delivered:2 app_limited busy:201ms rcv_rtt:202.512 rcv_space:115840 rcv_ssthresh:963295 minrtt:200.474
[root@localhost zorro]# ss -io state established '(dport = 80 or sport = 80)'
Netid Recv-Q Send-Q Local Address:Port Peer Address:Port Process
tcp 0 0 192.168.247.130:47864 192.168.247.129:http
ts sack cubic wscale:7,11 rto:603 rtt:200.748/75.374 ato:40 mss:1448 pmtu:1500 rcvmss:1448 advmss:1448 cwnd:10 bytes_sent:149 bytes_acked:150 bytes_received:48189440 segs_out:1619 segs_in:33282 data_segs_out:1 data_segs_in:33280 send 577.0Kbps lastsnd:2623 lastrcv:1 lastack:3 pacing_rate 1.2Mbps delivery_rate 57.8Kbps delivered:2 app_limited busy:201ms rcv_rtt:294.552 rcv_space:16550640 rcv_ssthresh:52423872 minrtt:200.474
[root@localhost zorro]# ss -io state established '(dport = 80 or sport = 80)'
Netid Recv-Q Send-Q Local Address:Port Peer Address:Port Process
tcp 0 0 192.168.247.130:47864 192.168.247.129:http
ts sack cubic wscale:7,11 rto:603 rtt:200.748/75.374 ato:40 mss:1448 pmtu:1500 rcvmss:1448 advmss:1448 cwnd:10 bytes_sent:149 bytes_acked:150 bytes_received:104552840 segs_out:2804 segs_in:72207 data_segs_out:1 data_segs_in:72205 send 577.0Kbps lastsnd:3221 lastack:601 pacing_rate 1.2Mbps delivery_rate 57.8Kbps delivered:2 app_limited busy:201ms rcv_rtt:286.159 rcv_space:25868520 rcv_ssthresh:52427352 minrtt:200.474
总结
从原理上看,一个延时大的网络不应该影响其带宽的利用。之所以大延时网络上的带宽利用率低,次要起因是延时变大之后,发送方发的数据不能及时达到接管方。导致发送缓存满之后,不能再继续发送数据。接管方则因为 TCP 通告窗口受到接管方残余缓存大小的影响。接管缓存小的话,则会通告对方发送窗口变小。进而影响发送方不能以大窗口发送数据。所以,这里的调优思路应该是,发送方调大 tcp_wmem,接管方调大 tcp_rmem。那么调成多大适合呢?如果咱们把大延时网络设想成一个缓存的话,那么缓存的大小应该是带宽延时(rtt)积。假如带宽为 1000Mbit/s,rtt 工夫为 400ms,那么缓存应该调整为大概 50Mbyte 左右。接管方 tcp_rmem 应该更大一些,以便在接受方不能及时处理数据的状况下,不至于产生残余缓存变小而影响通告窗口导致发送变慢的问题,能够思考调整为 2 倍的带宽延时积。在这个例子中就是 100M 左右。此时在原理上,tcp 的吞度量应该能达到高延时网络的带宽下限了。
然而网络环境自身很简单。首先:网络门路上的一堆网络设备自身会有肯定缓存。所以咱们大多数状况不必依照上述理论值调整本地的 tcp 缓存大小。其次,高延时网络个别随同着丢包几率高。当产生丢包的时候,带宽利用率低就不再只是缓存的影响了。此时拥塞管制自身会导致带宽利用率达不到要求。所以,抉择不同的拥塞控制算法,更多影响的是丢包之后的疾速复原过程和慢启动过程的成果。比方,bbr 这种对丢包不敏感的拥塞控制算法,在有丢包的状况下,对窗口的影响比其余拥塞控制算法更小。而如果网络仅仅是延时大,丢包很少的话,选什么拥塞控制算法对带宽利用率影响并不大,缓存影响会更大。