您现在的位置是:首页 >学无止境 >网络性能定位网站首页学无止境

网络性能定位

祥仔先生 2023-05-25 12:00:03
简介网络性能定位

根据tcp协议进行分析

目录

一、SYN_RECV阶段

1.1 确定半连接队列是否有丢包:

 1.2 确定全连接队列是否有丢包现象

二、close_wait

三、TIME_WAIT


一、SYN_RECV阶段

        内核在监听套接字的时候,在三次握手时,会创建两个队列,在服务器收到syn 包时,会创建半连接队列,并将这条握手未完成的连接 放到里面,然后回复ack,syn包给客户端,当客户端回复ack时,内核会将这条连接放到全连接队列里,调用accept就是将连接从全连接队列里取出

如果半连接队列或者全连接队列满了,则可能发生丢包行为

1.1 确定半连接队列是否有丢包:

dmesg | grep "TCP: drop open request from"

  半连接队列的连接数量也可以通过netstat命令统计SYN_RECV状态的连接得知。因为在3次握手时,收到syn包的连接的状态是SYN_RECV状态,而整个状态的持续时间是很短的,如果用netstat发现SYN_RECV状态的连接非常多,则说明半连接队列可能满了

netstat -ant|grep SYN_RECV|wc -l

        半连接队列的大小由/proc/sys/net/ipv4/tcp_max_syn_backlog控制,Linux的默认是1024

        当服务端发送SYN_ACK后将会开启一个定时器,如果超时没有收到客户端的ACK,将会重发SYN_ACK包。重传的次数由/proc/sys/net/ipv4/tcp_synack_retries控制,默认是5次

 1.2 确定全连接队列是否有丢包现象

netstat -s

netstat -s | egrep "listen|LISTEN" 
// 全连接队列溢出次数
667399 times the listen queue of a socket overflowed 
// 半连接队列溢出次数
667399 SYNs to LISTEN sockets dropped

ss -lnt

[root@mcs opt]# ss -lnt
State      Recv-Q Send-Q                        Local Address:Port                                       Peer Address:Port              
LISTEN     0      100                                       *:8080                                                  *:*

 在listen状态下,Send-Q表示全连接队列大小的最大值,Recv-Q表示全连接队列的使用大小,超过最大值则会溢出。

应用进程处理包缓慢,持续小于 网卡的接包速度的话,将导致全连接队列很快就满了,导致丢包

二、close_wait

 

服务端如果出现大量close_wait的话:

这个状态是由于客户端关闭了socket连接,发送了FIN报文,服务端也发送了ACK报文,此时客户端处于FIN_WAIT_2状态,服务端处于CLOSE_WAIT状态。并且可能存在服务端没有发送第二个FIN报文导致的。出现大量close_wait。

曾经遇到的问题:

        首先,我这边的大部分请求都需要查询数据库,我的数据库连接池设置的最大连接数是100,所以每一个请求创建了一个连接,等到100个请求就把连接池占满了,但是处理servlet的那个线程并没有释放这个连接,于是接下来的请求再去创建数据库连接的时候就会一直阻塞在那里,这里我所用的是DBCP作为连接池的,它的实现好像是使用apache的objectPool来实现的,如果没有可用的连接对象会导致线程等待,好了,servlet由于得不到数据库连接而阻塞了,这个客户端的请求就一直等待,客户端使用httpclient设置了5s的请求超时时间,那么超时之后就会抛出异常,关闭连接,关闭连接导致客户端发送了FIN报文,我这边的TCP/IP返回了ACK报文,但是由于处理请求的线程还处于阻塞的状态,所以当前的连接状态时CLOSE_WAIT。

三、TIME_WAIT

           参考TCP四次挥手原理,主动关闭连接的一方会出现 TIME_WAIT 状态,等待的时长为 2MSL(约1分钟左右)

        原因是:主动断开的一方回复 ACK 消息可能丢失,TCP 是可靠的传输协议,在没有收到 ACK 消息的另一端会重试,重新发送FIN消息,所以主动关闭的一方会等待 2MSL 时间,防止对方重试,这就出现了大量 TIME_WAIT 状态(参考: 四次挥手的最后两次)

需要注意:

1.每一个 time_wait 状态,都会占用一个「本地端口」,上限为 65535(16 bit,2 Byte);

2.当大量的连接处于 time_wait 时,新建立 TCP 连接会出错,address already in use : connect 异常

time_wait 状态的影响:

  • TCP 连接中,「主动发起关闭连接」的一端,会进入 time_wait 状态
  • time_wait 状态,默认会持续 2 MSL(报文的最大生存时间),一般是 2x2 mins
  • time_wait 状态下,TCP 连接占用的端口,无法被再次使用
  • TCP 端口数量,上限是 6.5w(65535,16 bit)
  • 大量 time_wait 状态存在,会导致新建 TCP 连接会出错,address already in use : connect 异常

2.现实场景:

  • 服务器端,一般设置:不允许「主动关闭连接」
  • 但 HTTP 请求中,http 头部 connection 参数,可能设置为 close,则,服务端处理完请求会主动关闭 TCP 连接
  • 现在浏览器中, HTTP 请求 connection 参数,一般都设置为 keep-alive
  • Nginx 反向代理场景中,可能出现大量短链接,服务器端,可能存在

3.解决办法:

  • 服务器端允许 time_wait 状态的 socket 被重用
  • 缩减 time_wait 时间,设置为 1 MSL(即,2 mins)

参考:

TCP的全连接和半连接队列

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。