内核TCP优化的相关参数

内核TCP相关的参数均在/proc/sys/net/ipv4下,有以下选项:

 

tcp_abort_on_overflow
tcp_adv_win_scale
tcp_allowed_congestion_control
tcp_app_win
tcp_autocorking
tcp_available_congestion_control
tcp_base_mss
tcp_challenge_ack_limit
tcp_congestion_control
tcp_dsack
tcp_early_retrans
tcp_ecn
tcp_fack
tcp_fastopen
tcp_fastopen_key
tcp_fin_timeout
tcp_frto
tcp_invalid_ratelimit
tcp_keepalive_intvl
tcp_keepalive_probes
tcp_keepalive_time
tcp_limit_output_bytes
tcp_low_latency
tcp_max_orphans
tcp_max_ssthresh
tcp_max_syn_backlog
tcp_max_tw_buckets
tcp_mem
tcp_min_tso_segs
tcp_moderate_rcvbuf
tcp_mtu_probing
tcp_no_metrics_save
tcp_notsent_lowat
tcp_orphan_retries
tcp_reordering
tcp_retrans_collapse
tcp_retries1
tcp_retries2
tcp_rfc1337
tcp_rmem
tcp_sack
tcp_slow_start_after_idle
tcp_stdurg
tcp_synack_retries
tcp_syncookies
tcp_syn_retries
tcp_thin_dupack
tcp_thin_linear_timeouts
tcp_timestamps
tcp_tso_win_divisor
tcp_tw_recycle
tcp_tw_reuse
tcp_window_scaling
tcp_wmem
tcp_workaround_signed_windows

ARP相关的内核参数,ARP缓存限制:

 

net.ipv4.neigh.default.gc_thresh1  在该值以下不gc,常驻缓存
net.ipv4.neigh.default.gc_thresh2  soft limit,可以临时超过该值,但只能保留5秒,之后会被gc掉
net.ipv4.neigh.default.gc_thresh3  hard limit,超过该值,就立即gc掉

在openstack网络节点中此参数很重要。

TCP相关的
连接数:
socket接收的所有连接都是存放在队列类型的数据结构中,有两类对列,可以自定义。

分别是下面两个内核参数:

 

/proc/sys/net/ipv4/tcp_max_syn_backlog
/proc/sys/net/core/somaxconn

其中:
tcp_max_syn_backlog: 半连接队列,是指定所能接受SYN同步包的最大客户端数量,即半连接上限;
somaxconn: 全连接队列,指服务端所能accept即处理数据的最大客户端数量,即完成连接上限。
tcp_max_syn_backlog:默认值为256
somaxconn:默认值为128,对应socket编程中的backlog,min{somaxconn,backlog}为实际值; 可以使用 ss -lnt中Recv-Q查看对列是否满

TCP三次握手过程

三次握手

 

状态转移图

上述是三次握手协议的过程,上述是协议流程。socket编程流程如下:

socket编程

  1. client发送syn(SYN_SENT状态),初始化一条连接,server收到syn之后进入SYN_RCVD的状态,并且此连接进入半连接队列(参数tcp_max_syn_backlog);同时回复syn+ack给client(第一次握手)。半连接队列满,新的连接请求就会被忽略。半连接攻击就是针对此资源的占用攻击;

  2. client收到server端发送的syn+ack后,进入ESTABLISHED状态,第二次握手完成,表示连接已建立(此时三次握手还未完成,server端状态未变)。同时回复ACK给server;

  3. server接收到ack后,进入ESTABLISHED的状态,第三次握手完成。同时建立的连接会放入全连接队列中,涉及参数(somaxconn);全连接队列满时,连接的处理取决于tcp_abort_on_overflow,为0: 丢弃ack,过一段时间重新发送syn+ack,tcp_synack_retries表示重试次数;1:发送 reset client 废弃此次连接;

  • somaxconn:全连接队列大小
  • tcp_abort_on_overflow: 队列满,ack的处理方式;
  • tcp_synack_retries: tcp_abort_on_overflow=0时,重发syn+ack的次数;受到半连接攻击时,可降低此值;
  • tcp_syncookies: 受到半连接攻击时,开启此参数

建立连接相关的参数

  • tcp_syn_retries(Default: 5): 发起syn连接,传输失败时重试的次数;重传间隔为2的次方,基础时间为3s

半连接相关参数

  • tcp_syncookies: 在Tcp服务器收到Tcp Syn包并返回Tcp Syn+ack包时,不专门分配一个数据区,而是根据这个Syn包计算出一个cookie值。在收到Tcp ack包时,Tcp服务器在根据那个cookie值检查这个Tcp ack包的合法性。如果合法,再分配专门的数据区进行处理未来的TCP连接。
    默认为0,1表示开启
  • tcp_synack_retries: 减少重试次数

连接维护阶段

  • tcp_keepalive_time: 如果在该参数指定的秒数内连接始终处于空闲状态,则内核向客户端发起对该主机的探测
  • tcp_keepalive_intvl: 探测包的间隔时间
  • tcp_keepalive_probes: 发送探测包的最大个数,如果发送完仍未回应,则释放连接
  • tcp_retries2: 已建立连接,未回复ACK 的数据包,最大尝试次数,尝试次数用尽后,释放连接
  • tcp_retries: 已建立的连接,未回复包的尝试次数,在尝试此次数之后,需要检查路由表
  • tcp_max_orphans: 系统所能处理不属于任何进程的TCP sockets最大数量。假如超过这个数量,那么不属于任何进程的连接会被立即reset
  • tcp_orphan_retries: 清理孤儿socket尝试次数

查看tcp连接各个状态的个数
netstat -n | grep "^tcp" | awk '{print $6}' | sort | uniq -c | sort -n

连接清理阶段

  1. 主动A的一方发送Fin,进入Fin_WAIT_1,接收到Fin端的P进入CLOSE_WAIT状态,P同时发送ack确认Fin;
  2. A接收到P发送的ack后进入Fin_WAIT_2半连接的状态;等待P侧的Fin消息;
  3. P侧发送Fin消息后,进入Last_ACK状态,同时A侧收到Fin后,发送ACK,同时进入TIME_WAIT状态;

注:
TIME_WAIT存在的原因:
1.可靠的终止TCP连接(确保另一侧能够收到ACK)。
2.保证让迟来的TCP报文段有足够的时间被识别并丢弃。
time_wait持续的时间: 2MSL,保证旧的数据能够丢弃。网络中的数据最大生命周期MSL(maxinum segment lifetime)

涉及参数:

  • tcp_max_tw_buckets(Default: 32768): 表示系统同时保持TIME_WAIT套接字的最大数量,如果超过这个数字,TIME_WAIT套接字将立刻被清除并打印警告信息
  • tcp_tw_recycle(Default: 0): 表示开启TCP连接中TIME-WAITsockets的快速回收,默认为0,表示关闭
  • tcp_tw_reuse(Default: 0): 表示开启重用。允许将TIME-WAITsockets重新用于新的TCP连接,默认为0,表示关闭
  • tcp_no_metrics_save(Default: 0): 一个tcp连接关闭后,把这个连接曾经有的参数比如慢启动门限snd_sthresh,拥塞窗口snd_cwnd 还有srtt等信息保存到dst_entry中, 只要dst_entry 没有失效,下次新建立相同连接的时候就可以使用保存的参数来初始化这个连接.。 默认为0,表示关闭
  • tcp_fin_timeout(Default: 60): TCP保持在FIN_WAIT_2状态的时间,超过这个时间,则tcp释放

拥塞控制及缓存管理
TCP支持多种拥塞处理机制:

  • tcp_allowed_congestion_control(cubic,reno): 系统允许的拥塞算法
  • tcp_available_congestion_control(cubic,reno): 系统可用的拥塞算法,是allowed子集
  • tcp_congestion_control(cubic): 建立tcp连接,默认的拥塞算法,socket编程时,设置TCP_CONGESTION指定拥塞算法

TCP的拥塞算法主要关注:“慢启动”/“拥塞避免”/“快速重传”/"快速恢复"等阶段。

  1. 滑动窗口协议

窗口大小决定了发送数据的长度,以提高TCP数据吞吐率,在此窗口的数据无需确认应答而可以继续发送直至窗口上限,再收到应答后将窗口滑动到确认应答序列号的位置,此时就可以继续发送。
窗口分为发送窗口和接收窗口,发送窗口表示可以发送的的数据量;接收窗口大小表示可以接收的数据。

以下是关于窗口大小的设置:

TCP发送方,发送缓存内的数据可以分为4类:

  • 已经发送并得到对端ACK
  • 已经发送但还未得到对端ACK
  • 未发送但对端允许发送的数据
  • 未发送且对端不允许发送的数据

其中"已经发送但还未得到对端ACK"和"未发送但对端允许发送的数据"称为发送窗口。
计算可以继续发送的数据,如已确认的序号为n,已发送到x字节,窗口大小为m,则可继续发送的数据量为:m-(n-x)

TCP接收方的缓存数据,分为3类:

  • 已接收
  • 未接收准备接收
  • 未接收未准备接收

其中,"未接收准备接收"称之为接收窗口。

窗口大小与接收端主机通知的窗口大小作比较,选择较小的值为发送窗口。

内核中涉及影响窗口大小的参数:

  • net.core.rmem_default = 212992: 每一个TCP socket默认的用于TCP接收的数据缓存大小,字节
  • net.core.rmem_max = 212992:每一个TCP socket最大的接收数据缓的最大存,字节;socket设置SO_RCVBUF不能超过此值。计算方式:max(87380, min(4MB, tcp_mem[1]*PAGE_SIZE/128))
  • net.core.wmem_default = 212992:每一个TCP socket默认的用于TCP发送数据的缓存大小,字节
  • net.core.wmem_max = 212992:每一个TCP socket最大的用于数据发送的缓存,字节;socket设置SO_SNDBUF不能超过此值。计算方式:max(65536, min(4MB, tcp_mem[1]*PAGE_SIZE/128))
    占用缓存的数据类型:
    接收缓存: 1. TCP缓存的无序的数据; 2. 已确认待应用读取的数据,剩下的是接收窗口最大可用大小。具体应用缓存所占比例参见:tcp_adv_win_scale。
    如何正确设置最大读缓存大小:
  • 若配置了tcp_adv_win_scale,读缓存的上限应当由最大的TCP接收窗口决定;
  • 网络带宽的大小,也影响能够接收的数据。因此,最大接收窗口也会依赖BDP设置(带宽延时积),如带宽为1Gbps,延时为2ms,则BDP:1*0.002=0.002Gb,最大接收窗口为0.002gb;
  • 根据tcp_adv_win_scale及最大接收窗口,设置合适的最大读缓存。

实际发送窗口都为接收窗口的大小。

由于每一个连接都会占用部分缓存,在长肥网络中BDP很大,导致最大缓存也会很大。连接数较多时,内存有限无法满足,此时就需要系统调整缓存大小,此功能需要显示的配置:
net.ipv4.tcp_moderate_rcvbuf = 1
默认tcp_moderate_rcvbuf配置为1,表示打开了TCP内存自动调整功能。若配置为0,则功能禁用。若设置了SO_SNDBUF、SO_RCVBUF,linux内核则不再对这样的连接执行自动调整功能。

内核在调整缓存时,涉及以下参数,单位都是页(4KB):

  • net.ipv4.tcp_mem = 378783 505045 757566
  • net.ipv4.tcp_rmem = 4096 87380 6291456
  • net.ipv4.tcp_wmem = 4096 16384 4194304

tcp_rmem

表示任何一个tcp连接的读缓存的上限值,分别表示:最小上限、初始上限(覆盖rmem_default)、最大上限。

tcp_wmem

参见tcp_rmem.

tcp_mem

其中,tcp_mem设定tcp内存的整天使用情况,定义了整体内存的无压力值、压力模式开启阀值、最大使用值。

  • 当整体内存小于tcp_mem[0]时,表示处于非压力模式下,每个tcp socket的最大缓存可增加至tcp_r/wmem[2];且新分配的内存一定会成功。
  • 当整体内存介于tcp_mem[0]和tcp_mem[1]之间,可能处于内存压力模式。每个tcp socket的缓存上限会减少。若tcp socket的内存小于tcp_rmem[0]/tcp_wmem[0],则内存分配会成功
  • 当整体内存介于tcp_mem[1]和tcp_mem[2]之间,一定处于内存压力模式下,行为如上
  • 当整体内存大于tcp_mem[2],则所有内存分配都会失败
  1. 慢启动

当主机开始发送数据时,如果发送大量数据可能会引起网络拥塞。因此引入一个慢启动的方法,在连接建立以后,设置窗口大小。

初始窗口大小设置规则:

  • mss > 1460*3: 设置为2MSS
  • mss > 1460: 设置为3MSS
  • 设置为4MSS,目前linux中默认为10MSS。

随着包的每次往返,发送窗口(cwnd)会以1、2、4指数函数的增长。如果发生拥塞,会导致拥塞加剧,因此引入慢启动阈值(ssthresh)的概念。当发生拥塞时,慢启动阈值会设置为cwnd/2(不小于2MSS)。这时tcp
由慢启动阶段转移至拥塞避免阶段。

ssthresh = max(在外的数据值/2, 2MSS)

丢包发生,重设ssthresh值,同时设置cwnd窗口大小:

  • Tahoe: 设置为1MSS,但是对于BDP较大的链路来说,链路利用率低。对于重复ACK引起的丢包,cwnd=ssthresh;
  1. 拥塞避免

慢启动阈值(ssthresh)初始值为很大。才传输初始阶段,首先会进入慢启动,发生拥塞之后,会确定新的ssthresh。进入拥塞避免阶段,cwnd设置为,以cwnd=cwnd+MSS*MSS/cwnd的方式增长,直至拥塞,在重新设置cwnd,进入慢启动。

  • cwnd < ssthresh: 慢启动阶段
  • cwnd > ssthresh: 拥塞避免阶段
  • cwnd = ssthresh;两个算法均可使用

TCP Reno引入了快速恢复和快速重传的算法:

  1. 快速重传

快速重传触发条件:收到三个相同的ACK。接收到收到乱序的数据包,如到达1、2、4、5序号的数据,可以判断丢失序号为3的数据,此时,接收端利用3个相同的ACK发给发送端,触发发送端快速重传。
快速重传:

  • ssthresh设置为cwnd的一半
  • cwnd设置为ssthresh+3MSS
  • 进入快速恢复阶段
  1. 快速恢复

由快速重传阶段进入快速恢复阶段,前两步与快速重传相同:

  • ssthresh设置为cwnd的一半
  • cwnd设置为ssthresh+3MSS
  • 每接收一个重复的ACK,cwnd值暂时增加1MSS
  • 每接收一个好的ACK,将cwnd重设为ssthresh,快速恢复过程结束

缓存相关参数:

  • tcp_adv_win_scale(Default: 1/centos): 表示应用缓存及TCP接受窗口的比列
    应用缓存: bytes/2^tcp_adv_win_scale 当tcp_adv_win_scale
    应用缓存: bytes-bytes/2^(-tcp_adv_win_scale)
    如上1, TCP接受窗口及应用缓存为接受缓存的各一半。
  • tcp_app_win: 表示预留maximum (window/2^tcp_app_win, mss) 字节给应用缓存。0表示不预留。

数据传输阶段

  • netdev_max_backlog(Default: 1000): 设备队列最大深度
  • tcp_base_mss(Default: 512): 一般是根据MTU值减去IP及tcp头部大小(共40字节)
  • tcp_mtu_probing(Default: 0): 是否开启MTU探测,默认关闭

MTU: Path MTU Discovery (PMTUD),发送DF置位的IP报文,如果中间设备回应ICMP分片不可达的消息,就逐渐减MTU,直到可以部分片到达目的地。如果中间设备不回应ICMP Type 3就无法使用。

在Centos7.2.1511版本中会经常发生系统crash,查看core dump文件是由于巨页导致(此版本的bug,7.3已修复),可关闭巨页:
配置文件位置:
/etc/kernel/mm/transparent_hugepage/enabled:
[always] madvise never:表示使用
always madvise [never]: 关闭
/etc/kernel/mm/transparent_hugepage/defrag:
[always] madvise never:表示使用
always madvise [never]: 关闭



作者:圣地亚哥_SVIP
链接:https://www.jianshu.com/p/4ea79f3c39a0
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章