tcp 相关总结

tcp 基础知识

这里写图片描述

  • CLOSED: 这个没什么好说的了,表示初始状态。
  • LISTEN: 这个也是非常容易理解的一个状态,表示服务器端的某个SOCKET处于监听状态,可以接受连接了。
  • SYN_RCVD: 这个状态表示接受到了SYN报文,在正常情况下,这个状态是服务器端的SOCKET在建立TCP连接时的三- 次握手会话过程中的一个中间状态,很短暂,基本上用netstat你是很难看到这种状态的,除非你特意写了一个客户端测试程序,故意将三次TCP握手过程中最后一个ACK报文不予发送。因此这种状态时,当收到客户端的ACK报文后,它会进入到ESTABLISHED状态。
  • SYN_SENT: 这个状态与SYN_RCVD遥想呼应,当客户端SOCKET执行CONNECT连接时,它首先发送SYN报文,因此也随即它会进入到了SYN_SENT状态,并等待服务端的发送三次握手中的第2个报文。SYN_SENT状态表示客户端已发送SYN报文。
  • ESTABLISHED:这个容易理解了,表示连接已经建立了。
  • FIN_WAIT_1: 这个状态要好好解释一下,其实FIN_WAIT_1和FIN_WAIT_2状态的真正含义都是表示等待对方的FIN报文。而这两种状态的区别是:FIN_WAIT_1状态实际上是当SOCKET在ESTABLISHED状态时,它想主动关闭连接,向对方发送了FIN报文,此时该SOCKET即进入到FIN_WAIT_1状态。而当对方回应ACK报文后,则进入到FIN_WAIT_2状态,当然在实际的正常情况下,无论对方何种情况下,都应该马上回应ACK报文,所以FIN_WAIT_1状态一般是比较难见到的,而FIN_WAIT_2状态还有时常常可以用netstat看到。
  • FIN_WAIT_2:上面已经详细解释了这种状态,实际上FIN_WAIT_2状态下的SOCKET,表示半连接,也即有一方要求close连接,但另外还告诉对方,我暂时还有点数据需要传送给你,稍后再关闭连接。
  • TIME_WAIT: 表示收到了对方的FIN报文,并发送出了ACK报文,就等2MSL后即可回到CLOSED可用状态了。如果FIN_WAIT_1状态下,收到了对方同时带FIN标志和ACK标志的报文时,可以直接进入到TIME_WAIT状态,而无须经过FIN_WAIT_2状态。
  • CLOSING: 这种状态比较特殊,实际情况中应该是很少见,属于一种比较罕见的例外状态。正常情况下,当你发送FIN报文后,按理来说是应该先收到(或同时收到)对方的ACK报文,再收到对方的FIN报文。但是CLOSING状态表示你发送FIN报文后,并没有收到对方的ACK报文,反而却也收到了对方的FIN报文。什么情况下会出现此种情况呢?其实细想一下,也不难得出结论:那就是如果双方几乎在同时close一个SOCKET的话,那么就出现了双方同时发送FIN报文的情况,也即会出现CLOSING状态,表示双方都正在关闭SOCKET连接。
  • CLOSE_WAIT: 这种状态的含义其实是表示在等待关闭。怎么理解呢?当对方close一个SOCKET后发送FIN报文给自己,你系统毫无疑问地会回应一个ACK报文给对方,此时则进入到CLOSE_WAIT状态。接下来呢,实际上你真正需要考虑的事情是察看你是否还有数据发送给对方,如果没有的话,那么你也就可以close这个SOCKET,发送FIN报文给对方,也即关闭连接。所以你在CLOSE_WAIT状态下,需要完成的事情是等待你去关闭连接。
  • LAST_ACK: 这个状态还是比较容易好理解的,它是被动关闭一方在发送FIN报文后,最后等待对方的ACK报文。当收到ACK报文后,也即可以进入到CLOSED可用状态了。

三次握手

这里写图片描述

四次挥手

这里写图片描述

数据接收

这里写图片描述
数据包的接收,从下往上经过了三层:网卡驱动、系统内核空间,最后到用户态空间的应用。Linux内核使用sk_buff(socketkernel buffers)数据结构描述一个数据包。当一个新的数据包到达,NIC(networkinterface controller)调用DMAengine,通过RingBuffer将数据包放置到内核内存区。RingBuffer的大小固定,它不包含实际的数据包,而是包含了指向sk_buff的描述符。当RingBuffer满的时候,新来的数据包将给丢弃。一旦数据包被成功接收,NIC发起中断,由内核的中断处理程序将数据包传递给IP层。经过IP层的处理,数据包被放入队列等待TCP层处理。每个数据包经过TCP层一系列复杂的步骤,更新TCP状态机,最终到达recvBuffer,等待被应用接收处理。有一点需要注意,数据包到达recvBuffer,TCP就会回ACK确认,既TCP的ACK表示数据包已经被操作系统内核收到,但并不确保应用层一定收到数据(例如这个时候系统crash),因此一般建议应用协议层也要设计自己的确认机制。

数据发送

这里写图片描述
和接收数据的路径相反,数据包的发送从上往下也经过了三层:用户态空间的应用、系统内核空间、最后到网卡驱动。应用先将数据写入TCP sendbuffer,TCP层将sendbuffer中的数据构建成数据包转交给IP层。IP层会将待发送的数据包放入队列QDisc(queueingdiscipline)。数据包成功放入QDisc后,指向数据包的描述符sk_buff被放入RingBuffer输出队列,随后网卡驱动调用DMAengine将数据发送到网络链路上。

tcp queue

  1. 当 client 通过 connect 向 server 发出 SYN 包时,client 会维护一个 socket 等待队列,而 server 会维护一个 SYN 队列

  2. 此时进入半链接的状态,如果 socket 等待队列满了,server 则会丢弃,而 client 也会由此返回 connection time out;只要是 client 没有收到 SYN+ACK,3s 之后,client 会再次发送,如果依然没有收到,9s 之后会继续发送

  3. 半连接 syn 队列的长度为 max(64, /proc/sys/net/ipv4/tcp_max_syn_backlog) 决定

  4. 当 server 收到 client 的 SYN 包后,会返回 SYN, ACK 的包加以确认,client 的 TCP 协议栈会唤醒 socket 等待队列,发出 connect 调用

  5. client 返回 ACK 的包后,server 会进入一个新的叫 accept 的队列,该队列的长度为 min(backlog, somaxconn),默认情况下,somaxconn 的值为 128,表示最多有 129 的 ESTAB 的连接等待 accept(),而 backlog 的值则由 int listen(int sockfd, int backlog) 中的第二个参数指定,listen 里面的 backlog 的含义请看这里。需要注意的是,一些 Linux 的发型版本会直接取 backlog 的值(本人目前接触到的都是正常的)。

  6. 当 accept 队列满了之后,即使 client 继续向 server 发送 ACK 的包,也会不被相应,此时,server 通过 /proc/sys/net/ipv4/tcp_abort_on_overflow 来决定如何返回,0 表示直接丢丢弃该 ACK,1 表示发送 RST 通知 client;相应的,client 则会分别返回 read timeout 或者 connection reset by peer。上面说的只是些理论,如果服务器不及时的调用 accept(),当 queue 满了之后,服务器并不会按照理论所述,不再对 SYN 进行应答,返回 ETIMEDOUT。

syn 半连接 accept 全连接

这里写图片描述

syn 半链接

三次握手协议中,服务器维护一个半连接队列,该队列为每个客户端的SYN包开设一个条目(服务端在接收到SYN包的时候,就已经创建了request_sock结构,存储在半连接队列中),该条目表明服务器已收到SYN包,并向客户发出确认,正在等待客户的确认包(会进行第二次握手发送SYN+ACK的包加以确认)。这些条目所标识的连接在服务器处于SYN_RECV状态,当服务器收到客户的确认包时,删除该条目,服务器进入ESTABLISHED状态。
该队列为SYN 队列,长度为 max(64, /proc/sys/net/ipv4/tcp_max_syn_backlog),在机器的tcp_max_syn_backlog值在/proc/sys/net/ipv4/tcp_max_syn_backlog下配置。

: 新版的 linux 已经没有了 syn 队列,SYN_RECV 直接存入 ehash table(e代表establish,ehash即已连接hash队列) 里面 ,详情见https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git

查看SYN queue 溢出

netstat -s | grep LISTEN
102324 SYNs to LISTEN sockets dropped

accept 全链接

当第三次握手时,当server接收到ACK之后,会进入一个新的accept的队列,该队列的长度为min(backlog, somaxconn),默认情况下,somaxconn ()的值为 128,表示最多有 129 的 ESTAB 的连接等待 accept();backlog 的值则应该创建socket 的时候由 int listen(int sockfd, int backlog) 中的第二个参数指定,listen 里面的 backlog 可以有我们的应用程序去定义的。

查看Accept queue 溢出

netstat -s | grep TCPBacklogDrop
TCPBacklogDrop: 2334

netstat ss 相关参数

ss -nltp 查看 listen 状态

Recv-Q 表示的当前等待服务端调用 accept 完成三次握手的 listen backlog 数值,也就是说,当客户端通过 connect() 去连接正在 listen() 的服务端时,这些连接会一直处于这个 queue 里面直到被服务端 accept()
Send-Q 表示的则是最大的 listen backlog 数值,这就就是上面提到的 min(backlog, somaxconn) 的值。

ss -ant |grep ESTAB 查看链接状态

recv-Q 表示网络接收队列
表示收到的数据已经在本地接收缓冲,但是还有多少没有被进程取走,recv()
如果接收队列Recv-Q一直处于阻塞状态,可能是遭受了拒绝服务 denial-of-service 攻击

send-Q 表示网路发送队列
对方没有收到的数据或者说没有Ack的,还是本地缓冲区.
如果发送队列Send-Q不能很快的清零,可能是有应用向外发送数据包过快,或者是对方接收数据包不够快

这两个值通常应该为0,如果不为0可能是有问题的。packets在两个队列里都不应该有堆积状态。可接受短暂的非0情况。

ss -s 统计信息

Total: 828 (kernel 0)
TCP:   20792 (estab 293, closed 20484, orphaned 0, synrecv 0, timewait 20480/0), ports 0

Transport Total     IP        IPv6
*     0         -         -        
RAW   0         0         0        
UDP   15        15        0        
TCP   308       306       2        
INET      323       321       2        
FRAG      0         0         0  
  • orphaned 不属于任何进程的TCP sockets 数量
  • synrecv 标识 syn 队列

    ss -i

    tcp   ESTAB      0      0                               10.132.*.*:ssh                                                                         10.132.*.*:18535   
     cubic wscale:9,9 rto:204 rtt:0.101/0.015 ato:40 mss:1460 cwnd:10 ssthresh:16 send 1156.4Mbps rcv_rtt:452153 rcv_space:45036
    tcp   ESTAB      0      0                                10.132.*.*:43630                                                                       10.135.*.*:6030    
     cubic wscale:9,9 rto:204 rtt:0.38/0.206 ato:40 mss:1460 cwnd:10 send 307.4Mbps rcv_rtt:398286 rcv_space:29862
    • rto 重传超时时间
    • rtt 往返时间
    • ato ACK超时时长
    • mss 网络传输数据最大值
    • mtu 网络传输最大报文包 mss+包头长度
    • cwnd 拥塞窗口
    • ssthresh 慢开始门限
    • rcv_space 可用于接收数据的缓冲区空间

netstat -s 统计信息


202270382 invalid SYN cookies received --- 三次握手ack包,syncookies校验没通过;
13700572 resets received for embryonic SYN_RECV sockets  ---syn_recv状态下,收到非重传的syn包,则返回reset

1123035 passive connections rejected because of time stamp ---开启sysctl_tw_recycle,syn包相应连接的时间戳 小于 路由中保存的时间戳;

14886782 failed connection attempts --- syn_recv状态下,socket被关闭; 或者  收到syn包(非重传)

438798 times the listen queue of a socket overflowed  ---收到三次握手ack包,accept队列满
438798 SYNs to LISTEN sockets ignored ---收到三次握手ack包,因各种原因(包括accept队列满) 创建socket失败

netstat -s | egrep "listen|LISTEN"

ss netstat 性能

time ss * , time netstat * 对比发现很明显 ss性能更高,一般建议使用 ss 命令

Net IO配置

系统配置

参数(路径+文件) 描述 默认值 优化值
/proc/sys/net/core/rmem_default 默认的TCP数据接收窗口大小(字节) 229376 256960
/proc/sys/net/core/rmem_max 最大的TCP数据接收窗口(字节) 131071 513920
/proc/sys/net/core/wmem_default 默认的TCP数据发送窗口大小(字节) 229376 256960
/proc/sys/net/core/wmem_max 最大的TCP数据发送窗口(字节) 131071 513920
/proc/sys/net/core/netdev_max_backlog 在每个网络接口接收数据包的速率比内核处理这些包的速率快时,允许送到队列的数据包的最大数目。 1000 2000
/proc/sys/net/core/somaxconn 定义了系统中每一个端口最大的accept队列的长度,这是个全局的参数。 128 2048
/proc/sys/net/core/optmem_max 表示每个套接字所允许的最大缓冲区的大小。 20480 81920
/proc/sys/net/ipv4/tcp_mem 确定TCP栈应该如何反映内存使用,每个值的单位都是内存页(通常是4KB)。第一个值是内存使用的下限;第二个值是内存压力模式开始对缓冲区使用应用压力的上限;第三个值是内存使用的上限。超过将报文丢弃,从而减少对内存的使用。对于较大的BDP可以增大这些值(注意,其单位是内存页而不是字节)。该值包括当前正在使用的所有TCP套接字 94011 125351 188022 131072 262144 524288
/proc/sys/net/ipv4/tcp_rmem 第一个值告诉内核每个TCP连接的最小接收缓冲区,并且该缓冲区总是分配给TCP套接字,即使系统处于高压状态。 指定的第二个值告诉内核为每个TCP套接字分配的默认接收缓冲区。该值将覆盖其他协议使用的rmem_default值。 此变量中指定的第三个和最后一个值指定可以为TCP套接字分配的最大接收缓冲区 4096 87380 4011232 8760 256960 4088000
/proc/sys/net/ipv4/tcp_wmem 为自动调优定义socket使用的内存。第一个值是为socket发送缓冲区分配的最少字节数;第二个值是默认值(该值会被wmem_default覆盖),缓冲区在系统负载不重的情况下可以增长到这个值;第三个值是发送缓冲区空间的最大字节数(该值会被wmem_max覆盖)。 4096 16384 4011232 8760 256960 4088000
/proc/sys/net/ipv4/tcp_keepalive_time TCP发送keepalive探测消息的间隔时间(秒),用于确认TCP连接是否有效。 7200 1800
/proc/sys/net/ipv4/tcp_keepalive_intvl 探测消息未获得响应时,重发该消息的间隔时间(秒)。 75 30
/proc/sys/net/ipv4/tcp_keepalive_probes 在认定TCP连接失效之前,最多发送多少个keepalive探测消息。 9 3
/proc/sys/net/ipv4/tcp_sack 启用有选择的应答(1表示启用),通过有选择地应答乱序接收到的报文来提高性能,让发送者只发送丢失的报文段,(对于广域网通信来说)这个选项应该启用,但是会增加对CPU的占用。 1 1
/proc/sys/net/ipv4/tcp_fack 启用转发应答,可以进行有选择应答(SACK)从而减少拥塞情况的发生,这个选项也应该启用。 1 1
/proc/sys/net/ipv4/tcp_timestamps CP时间戳(会在TCP包头增加12个字节),以一种比重发超时更精确的方法(参考RFC 1323)来启用对RTT 的计算,为实现更好的性能应该启用这个选项。 1 1
/proc/sys/net/ipv4/tcp_window_scaling 启用RFC 1323定义的window scaling,要支持超过64KB的TCP窗口,必须启用该值(1表示启用),TCP窗口最大至1GB,TCP连接双方都启用时才生效。 1 1
tcp_abort_on_overflow 当守护进程太忙而不能接受新的连接,就象对方发送reset消息,默认值是false。这意味着当溢出的原因是因为一个偶然的猝发,那么连接将恢复状态。只有在你确信守护进程真的不能完成连接请求时才打开该选项,该选项会影响客户的使用。(对待已经满载的sendmail,apache这类服务的时候,这个可以很快让客户端终止连接,可以给予服务程序处理已有连接的缓冲机会,所以很多防火墙上推荐打开它) 0 0
/proc/sys/net/ipv4/tcp_syncookies 表示是否打开TCP同步标签(syncookie),内核必须打开了CONFIG_SYN_COOKIES项进行编译,同步标签可以防止一个套接字在有过多试图连接到达时引起过载。目的是为了防止syn flood攻击 1 1
/proc/sys/net/ipv4/tcp_tw_reuse 表示是否允许将收到最后一个包超过1秒的处于TIME-WAIT状态的socket(TIME-WAIT的端口)用于新的TCP连接 。 0 1
/proc/sys/net/ipv4/tcp_tw_recycle 能够更快地回收TIME-WAIT套接字。2msl(RFC 793中规定MSL为2分钟) 变成 rto 0 1
/proc/sys/net/ipv4/tcp_fin_timeout 对于本端断开的socket连接,TCP保持在FIN-WAIT-2状态的时间(秒)。对方可能会断开连接或一直不结束连接或不可预料的进程死亡。 60 30
/proc/sys/net/ipv4/ip_local_port_range 表示TCP/UDP协议允许使用的本地端口号 32768 61000 1024 65000
/proc/sys/net/ipv4/tcp_max_syn_backlog 对于还未获得对方确认的连接请求,可保存在队列中的最大数目。如果服务器经常出现过载,可以尝试增加这个数字。 2048 16384
/proc/sys/net/ipv4/tcp_low_latency 允许TCP/IP栈适应在高吞吐量情况下低延时的情况,这个选项应该禁用。 0
/proc/sys/net/ipv4/tcp_westwood 启用发送者端的拥塞控制算法,它可以维护对吞吐量的评估,并试图对带宽的整体利用情况进行优化,对于WAN 通信来说应该启用这个选项。 0
/proc/sys/net/ipv4/tcp_bic 为快速长距离网络启用Binary Increase Congestion,这样可以更好地利用以GB速度进行操作的链接,对于WAN通信应该启用这个选项。 1
tcp_syn_retries 对于一个新建连接,内核要发送多少个 SYN 连接请求才决定放弃。不应该大于255,默认值是5,对应于180秒左右时间。。(对于大负载而物理通信良好的网络而言,这个值偏高,可修改为2.这个值仅仅是针对对外的连接,对进来的连接,是由tcp_retries1决定的) 5 1
tcp_synack_retries 对于远端的连接请求SYN,内核会发送SYN + ACK数据报,以确认收到上一个 SYN连接请求包。这是所谓的三次握手( threeway handshake)机制的第二个步骤。这里决定内核在放弃连接之前所送出的 SYN+ACK 数目。不应该大于255,默认值是5,对应于180秒左右时间。 5 1
/proc/sys/net/ipv4/netfilter/ip_conntrack_max 系统支持的最大ipv4连接数,默认65536(事实上这也是理论最大值),同时这个值和你的内存大小有关,如果内存128M,这个值最大8192,1G以上内存这个值都是默认65536 65535 65535
/proc/sys/net/ipv4/netfilter/ip_conntrack_tcp_timeout_established 已建立的tcp连接的超时时间,默认432000,也就是5天。影响:这个值过大将导致一些可能已经不用的连接常驻于内存中,占用大量链接资源,从而可能导致NAT ip_conntrack: table full的问题。建议:对于NAT负载相对本机的 NAT表大小很紧张的时候,可能需要考虑缩小这个值,以尽早清除连接,保证有可用的连接资源;如果不紧张,不必修改 432000 180
ip_conntrack_tcp_timeout_time_wait time_wait状态超时时间,超过该时间就清除该连接 120 120
ip_conntrack_tcp_timeout_close_wait close_wait状态超时时间,超过该时间就清除该连接 60 60
ip_conntrack_tcp_timeout_fin_wait fin_wait状态超时时间,超过该时间就清除该连接 120 120

/etc/sysctl.conf文件

/etc/sysctl.conf是一个允许你改变正在运行中的Linux系统的接口。它包含一些TCP/IP堆栈和虚拟内存系统的高级选项,可用来控制Linux网络配置,由于/proc/sys/net目录内容的临时性,建议把TCPIP参数的修改添加到/etc/sysctl.conf文件, 然后保存文件,使用命令“/sbin/sysctl –p”使之立即生效。具体修改方案参照上文:

fs.nr_open = 6553500
fs.file-max = 6553500
vm.swappiness = 1
net.ipv4.tcp_tw_recycle = 0
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_timestamps = 1
net.ipv4.ip_local_port_range = 11000 65535
net.core.netdev_max_backlog = 65535
net.core.somaxconn = 2048
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_max_syn_backlog = 262144
net.ipv4.tcp_max_tw_buckets = 20480
net.ipv4.tcp_no_metrics_save = 1
net.ipv4.tcp_syn_retries = 2
net.ipv4.tcp_synack_retries = 2
net.ipv4.tcp_mem = 94500000 915000000 927000000
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
net.ipv4.tcp_fin_timeout = 15
net.ipv4.tcp_keepalive_time = 15
net.ipv4.tcp_keepalive_probes = 3
net.ipv4.tcp_keepalive_intvl = 15

#disable ipv6
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
net.ipv6.conf.lo.disable_ipv6 = 1

业务配置

  • jdk serversocket 默认 50
  • netty 的 backlog 默认为 50
  • jetty 默认 50
  • tomcat backlog 是 AcceptCount参数,默认为100,Ali-Tomcat 为200.
  • nginx backlog 默认 511
    具体根据系统的并发量,吞吐量,以及长连接,短链接情况 进行设置.
    长连接 还要注意修改 fd 的上限(ulimit -n)

压测调优

慢慢调整io线程数,观察服务响应时间,第三方接口响应时间,关注io处理线程的状态:jstack 统计runnable的数量, 观察 cpu load (dstat,uptime,top),观察线程使用比例,观察listen等待队列是否有堆积, 观察 netstat -s |egrep listen 是否有增长. 结合各个参数的数据,和服务的具体功能,具体分析,慢慢优化参数.JAVA代码注意观察gc 的情况.
简单介绍几个场景的分析

  • 不依赖第三方,响应速度快

    • client 60 线程压测
    • 服务端 backlog 设置为 1024
    • 请求 是 http 短连接
    • 压力测试下,观察 io处理线程大多是wait 状态, listen 等待队列有积压, netstat -s accept 队列的overflowed次数没有增加,服务响应时间没有太大变化.cpu 使用率 不到40%,机器io,磁盘都没有到达瓶颈,gc正常
    • 对于响应很快的短连接服务 backlog 是很大的瓶颈 ,注意线程池不能盲目的给的很大,可能导致大量的 fetux 系统调用,和大量的cs , 中断 ,导致sys cpu 增加,反而影响性能.

    • 服务依赖第三方,响应快(小于2ms)
      TODO

    • 服务依赖第三方,响应慢 (大于50 ms)
      TODO
  • 不依赖第三方,响应慢(大于50ms)
    TODO

常见问题

syn rto, rto

  • 建立链接阶段,重传时间一般是1秒
  • 数据传输阶段,重传时间最小是200ms,windows是300ms,根据rtt动态变化,内网环境一般都是默认最小值
  • 可以通过ip route 修改rto, ip route change dev eth0 rto_min 20ms

syn flood 攻击

TIME_WAIT 导致连接数不够用

因为本地端口有限sysctl -a | grep net.ipv4.ip_local_port_range,
TIME_WAIT也有限sysctl -a | grep tcp_max_tw_buckets
常见都是打开tcp_tw_recycle,tcp_tw_reuse
- tcp_tw_reuse开启重用,重用生效条件
1)tcp_tw_reuse选项和tcp_timestamps选项也必须同时打开;
2)重用TIME_WAIT的条件是收到最后一个包后超过1s。
-tcp_tw_recycle 快速回收

一、net.ipv4.tcp_tw_recycle
功能:系统会假设对端开启了 tcp_timestamps,然后会去比较时间戳,如果时间戳变大了,就可以重用fd。开启后在 3.5*RTO 内回收,RTO 200ms~ 120s 具体时间视网络状况。内网状况比tw_reuse 稍快,公网尤其移动网络大多要比tw_reuse 慢,优点就是能够回收服务端的TIME_WAIT数量
打开后的副作用:如果对端是一个NAT网络的话(如:一个公司只用一个IP出公 网)或是对端的IP被另一台重用了,或者一个机器上多个docker Node,这个事就复杂了。建链接的SYN可能就被直接丢掉了(你可能会看到connection time out的错误) -- 这就是为什么一个公司连接在测试服务器时,会很慢,但是服务器本身负载很低的问题的原因。
建议:对外服务器不能打开该开关!!!对内服务器如果没有docker(Nat转换)的可以打开。
二、net.ipv4.tcp_timestamps
功能:记录数据包的发送时间,其为双向的选项,当一方不开启时,两方都将停用timestamps,默认打开
打开后的副作用:1.10字节的TCP header开销 2. allow you to guess the uptime of a target system
建议:保持打开状态

三、net.ipv4.tcp_tw_reuse
功能:表示开启重用。当从协议认为安全,则允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭。 协议什么情况下认为是安全的?从代码来看是收到最后一个包后超过1s :(sysctl_tcp_tw_reuse && get_seconds() - tcptw->tw_ts_recent_stamp > 1)
打开后的副作用:主动关闭方有可能在下次使用时收到上一次连接的数据包,包括关闭连接响应包或者正常通信的数据包(因为数据包需要经过MSL时间才会消失),主动关闭方会出现奇怪的表现。
建议:服务器应该打开该配置,因为内网网络状态一般比较好。

tcp_tw_recycle 导致syn抛弃

启用TIME-WAIT状态sockets的快速回收,这个选项不推荐启用。在NAT(Network Address Translation)网络下,会导致大量的TCP连接建立错误。当开启了tcp_tw_recycle选项后,当连接进入TIME_WAIT状态后,会记录对应远端主机最后到达分节的时间戳。如果同样的主机有新的分节到达,且时间戳小于之前记录的时间戳,即视为无效,相应的数据包会被丢弃.

半链接队列导致 syn丢包

在Linux内核2.2之后,分离为两个backlog来分别限制半连接(SYN_RCVD状态)队列大小和全连接(ESTABLISHED状态)队列大小。
SYN queue 队列长度由 /proc/sys/net/ipv4/tcp_max_syn_backlog 指定,默认为2048。
Accept queue 队列长度由 /proc/sys/net/core/somaxconn 和使用listen函数时传入的参数,二者取最小值。默认为128。在Linux内核2.4.25之前,是写死在代码常量 SOMAXCONN ,在Linux内核2.4.25之后,在配置文件 /proc/sys/net/core/somaxconn 中直接修改,或者在 /etc/sysctl.conf 中配置 net.core.somaxconn = 128 。溢出时,根据tcp_abort_on_overflow处理,tcp_abort_on_overflow指定为1(将返回RST),否则底层将不会做任何事情……这是一种委婉的退让策略,在服务端处理不过来时,让客户端误以为ACK丢失,继续重新发送ACK。这样,当服务端的处理能力恢复时,这条连接又可以重新被移动到ESTABLISHED队列中去。

CLOSE_WAIT 过多

CLOSE_WAIT 看状态图是被动关闭方的状态,在客户端关闭链接时,服务端没有做响应导致大量CLOSE_WAIT.

  • 因为closewait不会主动回收,一般只能通过重启,
  • keepalive 参数解决closewait问题,会定时心跳client,client关闭会返回rst关闭链接
  • 应用程序问题:如果在应用程序中忘记了 close 相应的TCP连接,那么Y也就不会发出 FIN 包,进而导致 CLOSE_WAIT;
  • 应用程序响应太慢:应用程序不能及时响应client的关闭请求也会导致CLOSE_WAIT状态的堆积。
  • Accept backlog太大:Accept 的 backlog太大,设想突然遭遇大访问量的话,即便响应速度不慢,也可能出现来不及消费的情况,导致多余的请求还在队列里就被对方关闭了。

Refrence

发布了63 篇原创文章 · 获赞 32 · 访问量 11万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览