那么,设置好最大缓存限制后就高枕无忧了吗?对于一个TCP连接来说,可能已经充分利用网络资源,使用大窗口、大缓存来保持高速传输了。比如在长肥网络中,缓存上限可能会被设置为几十兆字节,但系统的总内存却是有限的,当每一个连接都全速飞奔使用到最大窗口时,1万个连接就会占用内存到几百G了,这就限制了高并发场景的使用,公平性也得不到保证。我们希望的场景是,在并发连接比较少时,把缓存限制放大一些,让每一个TCP连接开足马力工作;当并发连接很多时,此时系统内存资源不足,那么就把缓存限制缩小一些,使每一个TCP连接的缓存尽量的小一些,以容纳更多的连接。
linux为了实现这种场景,引入了自动调整内存分配的功能,由tcp_moderate_rcvbuf配置决定,如下:
- net.ipv4.tcp_moderate_rcvbuf = 1
默认tcp_moderate_rcvbuf配置为1,表示打开了TCP内存自动调整功能。若配置为0,这个功能将不会生效(慎用)。
另外请注意:当我们在编程中对连接设置了SO_SNDBUF、SO_RCVBUF,将会使linux内核不再对这样的连接执行自动调整功能!
那么,这个功能到底是怎样起作用的呢?看以下配置:
- net.ipv4.tcp_rmem = 8192 87380 16777216
- net.ipv4.tcp_wmem = 8192 65536 16777216
- net.ipv4.tcp_mem = 8388608 12582912 16777216
tcp_rmem[3]数组表示任何一个TCP连接上的读缓存上限,其中 tcp_rmem[0]表示最小上限, tcp_rmem[1]表示初始上限(注意,它会覆盖适用于所有协议的rmem_default配置), tcp_rmem[2]表示最大上限。
tcp_wmem[3]数组表示写缓存,与tcp_rmem[3]类似,不再赘述。
tcp_mem[3]数组就用来设定TCP内存的整体使用状况,所以它的值很大(它的单位也不是字节,而是页–4K或者8K等这样的单位!)。这3个值定义了TCP整体内存的无压力值、压力模式开启阀值、最大使用值。以这3个值为标记点则内存共有4种情况:
1、当TCP整体内存小于tcp_mem[0]时,表示系统内存总体无压力。若之前内存曾经超过了tcp_mem[1]使系统进入内存压力模式,那么此时也会把压力模式关闭。
这种情况下,只要TCP连接使用的缓存没有达到上限(注意,虽然初始上限是tcp_rmem[1],但这个值是可变的,下文会详述),那么新内存的分配一定是成功的。
2、当TCP内存在tcp_mem[0]与tcp_mem[1]之间时,系统可能处于内存压力模式,例如总内存刚从tcp_mem[1]之上下来;也可能是在非压力模式下,例如总内存刚从tcp_mem[0]以下上来。
此时,无论是否在压力模式下,只要TCP连接所用缓存未超过tcp_rmem[0]或者tcp_wmem[0],那么都一定都能成功分配新内存。否则,基本上就会面临分配失败的状况。(注意:还有一些例外场景允许分配内存成功,由于对于我们理解这几个配置项意义不大,故略过。)
3、当TCP内存在tcp_mem[1]与tcp_mem[2]之间时,系统一定处于系统压力模式下。其他行为与上同。
4、当TCP内存在tcp_mem[2]之上时,毫无疑问,系统一定在压力模式下,而且此时所有的新TCP缓存分配都会失败。