netstat 和ss
用 netstat 或者 ss ,来查看套接字、网络栈、网络接口以及路由表的信息,个人更推荐,使用 ss 来查询网络的连接信息,因为它比 netstat 提供了更好的性能(速度更快)。
使用示例:
# head -n 3 表示只显示前面3行 # -l 表示只显示监听套接字 # -n 表示显示数字地址和端口(而不是名字) # -p 表示显示进程信息 $ netstat -nlp | head -n 3
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:24007 0.0.0.0:* LISTEN 14447/glusterd
# -l 表示只显示监听套接字 # -t 表示只显示 TCP 套接字 # -n 表示显示数字地址和端口(而不是名字) # -p 表示显示进程信息 $ ss -ltnp | head -n 3
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 10 *:24007 *:* users:(("glusterd",pid=14447,fd=10))
LISTEN 0 128 127.0.0.1:10248 *:* users:(("kubelet",pid=9901,fd=27))
关于Recv-Q 和 Send-Q
一、当套接字处于连接状态(Established)时
Recv-Q 表示套接字缓冲还没有被应用程序取走的字节数(即接收队列长度)。而 Send-Q 表示还没有被远端主机确认的字节数(即发送队列长度)。
二、当套接字处于监听状态(Listening)时
Recv-Q 表示当前全连接队列使用的长度。而 Send-Q 表示全连接队列的最大长度。
所谓全连接,是指服务器收到了客户端的 ACK,完成了 TCP 三次握手,然后就会把这个连接挪到全连接队列中。这些全连接中的套接字,还需要被 accept() 系统调用取走,服务器才可以开始真正处理客户端的请求。
与全连接队列相对应的,还有一个半连接队列。所谓半连接是指还没有完成 TCP 三次握手的连接,连接只进行了一半。服务器收到了客户端的 SYN 包后,就会把这个连接放到半连接队列中,然后再向客户端发送 SYN+ACK 包。
协议栈统计信息
$ netstat -s ... Tcp: 3244906 active connection openings 23143 passive connection openings 115732 failed connection attempts 2964 connection resets received 1 connections established 13025010 segments received 17606946 segments sent out 44438 segments retransmitted 42 bad segments received 5315 resets sent InCsumErrors: 42 ... $ ss -s Total: 186 (kernel 1446) TCP: 4 (estab 1, closed 0, orphaned 0, synrecv 0, timewait 0/0), ports 0 Transport Total IP IPv6 * 1446 - - RAW 2 1 1 UDP 2 2 0 TCP 4 3 1 ...
这些协议栈的统计信息都很直观。ss 只显示已经连接、关闭、孤儿套接字等简要统计,而 netstat 则提供的是更详细的网络协议栈信息。
全/半连接队列溢出的次数查看
netstat -s | egrep "listen|LISTEN" 8111 times the listen queue of a socket overflowed
10771 SYNs to LISTEN sockets dropped
第一行表示全连接队列的溢出,第二行表示半连接队列溢出,注意这是一个累加的结果,看的是增量而不是总量
上面的命令每隔几秒执行一次如果数字在增加说明队列满了已经溢出
关于全连接队列和半连接队列
半连接队列 syns queue
全连接队列 accept queue
三次握手中,在第一步server收到client的syn后,把这个连接信息放到半连接队列中,同时回复syn+ack给client(第二步);
syn flood 攻击就是针对半连接队列的,攻击方不停建立连接但是只做第一步,第二部收到ack+syn后直接丢弃导致server上的半连接队列满了无法正常接受其他正常请求。
第三步的时候server收到client的ack,如果这时全连接队列没满,那么从半连接队列拿出这个连接的信息放入到全连接队列中,否则按tcp_abort_on_overflow指示的执行。
全连接队列的相关的性能瓶颈
简单来说TCP三次握手后有个accept队列,进到这个队列才能从Listen变成accept,默认backlog 值是50,很容易就满了。满了之后握手第三步的时候server就忽略了client发过来的ack包(隔一段时间server重发握手第二步的syn+ack包给client),如果这个连接一直排不上队就异常了。
全连接队列的大小取决于:min(backlog, somaxconn) . backlog是在socket创建的时候传入的,somaxconn是一个os级别的系统参数。
写代码的时候从来没有想过这个backlog或者说大多时候就没给他值(那么默认就是50)
全连接队列、半连接队列溢出这种问题很容易被忽视,但是又很关键,特别是对于一些短连接应用(比如Nginx、PHP,当然他们也是支持长连接的)更容易爆发。 一旦溢出,从cpu、线程状态看起来都比较正常,但是压力上不去,在client看来rt也比较高(rt=网络+排队+真正服务时间),但是从server日志记录的真正服务时间来看rt又很短。
jdk、netty等一些框架默认backlog比较小,可能有些情况下导致性能上不去。
参考:https://mp.weixin.qq.com/s/yH3PzGEFopbpA-jw4MythQ