你想通过执行ping google.com来判断网络连通性么?
由于nio
的普及,ck10k
的问题已经成为过去式。现在随便一台服务器,就可以支持数十万级别的连接了。那么我们来算一下,100万的连接需要多少资源。
首先,每一个连接都是文件句柄,所以需要文件描述符数量支持才行,每一个socket内存占用15k-20k之间,这样,仅维护相应socket,就需要20G
内存;而广播一个1KB的消息需要占用的带宽为1000M
!
查看当前系统的连接
如何看当前系统有多少连接呢?可以使用netstat
结合awk
进行统计。如下脚本,统计了每一种状态的tcp连接数量
# netstat -antp | awk '{a[$6]++}END{ for(x in a)print x,a[x]}'
LISTEN 41
CLOSE_WAIT 24
ESTABLISHED 150
Foreign 1
TIME_WAIT 92
netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
(
netstat -n | awk ‘/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}’
TIME_WAIT 8947 等待足够的时间以确保远程TCP接收到连接中断请求的确认
FIN_WAIT1 15 等待远程TCP连接中断请求,或先前的连接中断请求的确认
FIN_WAIT2 1 从远程TCP等待连接中断请求
ESTABLISHED 55 代表一个打开的连接
SYN_RECV 21 再收到和发送一个连接请求后等待对方对连接请求的确认
CLOSING 2 没有任何连接状态
LAST_ACK 4 等待原来的发向远程TCP的连接中断请求的确认
TCP连接状态详解
LISTEN: 侦听来自远方的TCP端口的连接请求
SYN-SENT: 再发送连接请求后等待匹配的连接请求
SYN-RECEIVED:再收到和发送一个连接请求后等待对方对连接请求的确认
ESTABLISHED: 代表一个打开的连接
FIN-WAIT-1: 等待远程TCP连接中断请求,或先前的连接中断请求的确认
FIN-WAIT-2: 从远程TCP等待连接中断请求
CLOSE-WAIT: 等待从本地用户发来的连接中断请求
CLOSING: 等待远程TCP对连接中断的确认
LAST-ACK: 等待原来的发向远程TCP的连接中断请求的确认
TIME-WAIT: 等待足够的时间以确保远程TCP接收到连接中断请求的确认
CLOSED: 没有任何连接状态
)
LAST_ACK 5
SYN_RECV 30
ESTABLISHED 1597
FIN_WAIT1 51
FIN_WAIT2 504
TIME_WAIT 1057
其中的
SYN_RECV表示正在等待处理的请求数;
ESTABLISHED表示正常数据传输状态;
TIME_WAIT表示处理完毕,等待超时结束的请求数。
但如果你在一台有上万连接的服务器上执行这个命令,你可能会等上很长时间。所以,我们有了第二代网络状态统计工具:netstat
=> ss
(可别和那个越狱工具搞混了)。
# ss -s
Total: 191 (kernel 220)
TCP: 5056 (estab 42, closed 5000, orphaned 3, synrecv 0, timewait 5000/0), ports 3469
...
netstat
属于net-tools
工具集,而ss
属于iproute
。其命令对应如下,是时候和net-tools说Bye了。
用途 |
net-tools |
iproute |
---|---|---|
统计 |
ifconfig |
ss |
地址 |
netstat |
ip addr |
路由 |
route |
ip route |
邻居 |
arp |
ip neigh |
VPN |
iptunnel |
ip tunnel |
VLAN |
vconfig |
ip link |
组播 |
ipmaddr |
ip maddr |
ss命令
基本使用
我们按照使用场景来看下ss的用法。
ss是Socket Statistics的缩写,与netstat命令一样用户查看套接字的.但是ss比netstat更加的先进.
- ss速度更快,ss不再像netstat遍历
/proc/pid
目录获取信息了,而是读取/proc/net/
,服务器上上规模的sockets时,ss的速度很明显。 - ss输出的信息更加详细,比如定时器,内存,sockets管理信息等,对调优很有帮助。
- ss提供的精确的过滤功能,一般人不会用,用的人不一般。
像netstat一样使用ss
ss提供了与netstat一样的选项,所以从netstat迁移到ss很平滑.
选项 | 含义 |
---|---|
-a | 把listen与not-listen的都显示出来,默认只显示established状态的sockets |
-l | only listening |
-p | 显示进行相关信息,ss显示的比较全 |
-n | 数字形式 |
-t/u/x/w | tcp/udp/unix/raw |
ss -s
查看sockets分布
查看当前有多少个sockets连接,每个状态分别是多少,每次了解系统连接时,
$ ss -s
TCP: 49 (estab 3, closed 1, orphaned 39, synrecv 0, timewait 0/0), ports 0
Transport Total IP IPv6
* 0 - -
* RAW 0 0 0
* UDP 8 8 0
* TCP 48 48 0
* INET 56 56 0
* FRAG 0 0 0
如果ss命令搞来搞去感觉很麻烦,请记住ss -s
.
更加精细的输出控制
选项 | 含义 |
---|---|
-i | tcp内部详细信息 |
-e | socket详细信息 |
-o | 定时器信息 |
-m | socket内存使用情况 |
todo: 进行实例说明
NB的过滤功能
根据state进行过滤
ss state established
listen, established , syn-sent , syn-recv , fin-wait-1 , fin-wait-2 , time-wait , closed , close-wait , last-ack , closing
全能命令:
查看系统正在监听的tcp连接
ss -atr
ss -atn #仅ip
查看系统中所有连接
ss -alt
查看监听444端口的进程pid
ss -ltp | grep 444
查看进程555占用了哪些端口
ss -ltp | grep 555
显示所有udp连接
ss -u -a
查看TCP sockets,使用-ta选项 查看UDP sockets,使用-ua选项 查看RAW sockets,使用-wa选项 查看UNIX sockets,使用-xa选项
和某个ip的所有连接
ss dst 10.66.224.130
ss dst 10.66.224.130:http
ss dst 10.66.224.130:smtp
ss dst 10.66.224.130:443
显示所有的http连接
ss dport = :http
查看连接本机最多的前10个ip地址
netstat -antp | awk '{print $4}' | cut -d ':' -f1 | sort | uniq -c | sort -n -k1 -r | head -n 10
Recv-Q和Send-Q
注意ss的执行结果,我们说明一下Recv-Q和Send-Q。
这两个值,在LISTEN
和ESTAB
状态分别代表不同意义。一般,正常的应用程序这两个值都应该为0(backlog除外)。数值越大,说明问题越严重。
LISTEN状态
- Recv-Q:代表建立的连接还有多少没有被accept,比如Nginx接受新连接变的很慢
- Send-Q:代表listen backlog值
ESTAB状态
- Recv-Q:内核中的数据还有多少(bytes)没有被应用程序读取,发生了一定程度的阻塞
- Send-Q:代表内核中发送队列里还有多少(bytes)数据没有收到ack,对端的接收处理能力不强
查看网络流量
查看流量
有很多工具可以看网络流量,但我最喜欢sar。sar是linux上功能最全的监控软件。如图,使用sar -n DEV 1
即可每秒刷新一次网络流量。
当然,你也可以使用ifstat、nload、iptraf等命令查看。然而数据来源,还是来自我们的/proc目录
watch cat /proc/net/dev
查看占流量最大的IP
有时候我们发现网络带宽占用非常高,但我们无法判断到底流量来自哪里。这时候,iftop就可以帮上忙了。如图,可以很容易的找出流量来自哪台主机。
当你不确定内网的流量来源,比如有人在压测,api调用不合理等,都可以通过这种方法找到他。
抓包
tcpdump
当我们需要判断是否有流量,或者调试一个难缠的netty应用问题,则可以通过抓包的方式去进行进一步的判断。在Linux上,可以通过tcpdump
命令抓取数据,然后使用Wireshark
进行分析。
tcpdump -i eth0 -nn -s0 -v port 80
-i
指定网卡进行抓包-n
和ss一样,表示不解析域名-nn
两个n表示端口也是数字,否则解析成服务名-s
设置抓包长度,0表示不限制-v
抓包时显示详细输出,-vv、-vvv依次更加详细
1)加入-A
选项将打印ascii ,-X
打印hex码。
tcpdump -A -s0 port 80
2)抓取特定ip的相关包
tcpdump -i eth0 host 10.10.1.1
tcpdump -i eth0 dst 10.10.1.20
3)-w
参数将抓取的包写入到某个文件中
tcpdump -i eth0 -s0 -w test.pcap
4)tcpdump支持表达式,还有更加复杂的例子,比如抓取系统中的get,post请求(非https)
tcpdump -s 0 -v -n -l | egrep -i "POST /|GET /|Host:"
更多参见 https://hackertarget.com/tcpdump-examples/
抓取的数据,使用wireshark查看即可。
http抓包
抓包工具将自身当作代理,能够抓取你的浏览器到服务器之间的通讯,并提供修改、重放、批量执行的功能。是发现问题,分析协议,攻击站点的利器。常用的有以下三款:
- Burpsuite (跨平台)
- Fiddle2 (Win)
- Charles (Mac)
坏事要偷偷的干哦。
流量复制
你可能需要使你的生产环境HTTP真实流量在开发环境或者预演环境重现,这样就用到了流量复制功能。 有三个工具可供选择,个人倾向于Gor。
- Gor
- TCPReplay
- TCPCopy
连接数过多问题
根据TCP/IP介绍,socket大概包含10个连接状态。我们平常工作中遇到的,除了针对SYN的拒绝服务攻击,如果有异常,大概率是TIME_WAIT和CLOSE_WAIT的问题。 TIME_WAIT一般通过优化内核参数能够解决;CLOSE_WAIT一般是由于程序编写不合理造成的,更应该引起开发者注意。
TIME_WAIT
TIME_WAIT是主动关闭连接的一方保持的状态,像nginx、爬虫服务器,经常发生大量处于time_wait状态的连接。TCP一般在主动关闭连接后,会等待2MS
,然后彻底关闭连接。由于HTTP使用了TCP协议,所以在这些频繁开关连接的服务器上,就积压了非常多的TIME_WAIT状态连接。
某些系统通过dmesg可以看到以下信息。
__ratelimit: 2170 callbacks suppressed
TCP: time wait bucket table overflow
TCP: time wait bucket table overflow
TCP: time wait bucket table overflow
TCP: time wait bucket table overflow
通过ss -s命令查看,可以看到timewait已经有2w个了。
ss -s
Total: 174 (kernel 199)
TCP: 20047 (estab 32, closed 20000, orphaned 4, synrecv 0, timewait 20000/0), ports 10785
sysctl命令可以设置这些参数,如果想要重启生效的话,加入/etc/sysctl.conf文件中。
# 修改阈值,tcp_timestamps是最核心的参数,其他参数是依赖于此
net.ipv4.tcp_timestamps = 1
net.ipv4.tcp_max_tw_buckets = 50000
# 表示开启TCP连接中TIME-WAIT sockets的快速回收
net.ipv4.tcp_tw_reuse = 1
#启用timewait 快速回收。这个一定要开启,默认是关闭的。
net.ipv4.tcp_tw_recycle= 1
# 修改系統默认的TIMEOUT时间,默认是60s
net.ipv4.tcp_fin_timeout = 10
测试参数的话,可以使用 sysctl -w net.ipv4.tcp_tw_reuse = 1 这样的命令。如果是写入进文件的,则使用sysctl -p生效。
#永久生效 echo net.ipv4.tcp_tw_reuse=1 >> /etc/sysctl.conf;echo net.ipv4.tcp_tw_recycle=1 >> /etc/sysctl.conf; echo net.ipv4.tcp_timestamps=1 >> /etc/sysctl.conf;echo net.ipv4.tcp_max_tw_buckets=50000 >> /etc/sysctl.conf; echo net.ipv4.tcp_fin_timeout=10 >> /etc/sysctl.conf; sysctl -p #查看 sysctl -a|grep -E "net.ipv4.(tcp_tw_reuse|tcp_tw_recycle|tcp_timestamps|tcp_fin_timeout|tcp_max_tw_buckets)"
CLOSE_WAIT
CLOSE_WAIT一般是由于对端主动关闭,而我方没有正确处理的原因引起的。说白了,就是程序写的有问题,属于危害比较大的一种。
我们拿”csdn 谐音太郎”遇到的一个典型案例来说明。
代码是使用HttpClient的一个使用片段。在这段代码里,通过调用in.close()来进行连接资源的清理。但可惜的是,代码中有一个判断:非200状态的连接直接返回null。在这种情况下,in
连赋值的机会都没有,当然也就无法关闭,然后就发生了连接泄漏。
所以,HttpClient的正确关闭方式是使用其api:abort()
。
其他常用命令
应用软件
# 断点续传下载文件
wget -c $url
# 下载整站
wget -r -p -np -k $url
# 发送网络连接(常用)
curl -XGET $url
# 传输文件
scp
sftp
# 数据镜像备份
rsync
检测工具
# 连通性检测
ping google.com
# 到对端路由检测
tracepath google.com
# 域名检测
dig google.com
nslookup google.com
# 网络扫描工具
nmap
# 压力测试
iperf
# 全方位监控工具(好东西)
nmon
iftop
vmstat
配置工具
# 停止某个网卡 ifdown # 开启某个网卡 ifup # 多功能管理工具 ethtool
压力测试
wrk ab webbench http_load
多功能工具
# 远程登录
telnet
ssh
nc
# 防火墙
iptables -L
结尾
除了基本的工具,本文提到的很多网络命令,都不是预装的,需要使用yum自行安装。网络编程方面的学习,我觉得,读一下《TCP/IP详解 卷1:协议》这本书,然后写几个Netty应用就可以了。
NIO我们已经在I/O篇提起了,在此不再做详细介绍。等你碰到所谓的拆包粘包问题,遇到心跳和限流问题,甚至遇到了流量整形问题,那么证明你离一个专业的网络编程程序员越来越近了。