为什么会出现 close wait
数据库连接不释放问题实际上上线之前就会发现,线上系统出这种问题根本就是测试不过关。
close wait 出现场景
我之前也遇到过出现很多CLOSE_WAIT的场景,一般出现这种情况,都是同步通信的场景,server端执行业务超时,client端主动断开连接的场景。
特别是一些写库的操作,随着表越来越大,写操作越来越慢,当慢到一定程序后会触发client端的超时再重试机制,越来越多的写操作积压在server端,很短的时间内server端被搞的socket全是CLOSE_WAIT,只能重启,重启也是好一段时间。
所以现在我们现在对同步通信机制一定要保证不能有不可预估的开销。这类开销一律走异步机制
检查tcp方法
lsof -i:8081
查看所有tcp连接
netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
netstat -n | grep 8082 | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
按类型查看 tcp 连接
netstat -nap |grep :8082| grep CLOSE_WAIT
查看close_wait的tcp连接数量
netstat -atn|grep CLOSE_WAIT|wc -l
netstat -nap |grep :8082| grep CLOSE_WAIT | wc -l
临时解决方法
设置 tcp 连接
close_wait 状态出现的原因是被动关闭方未关闭 socket 造成
当客户端因为某种原因先于服务端发出了 FIN 信号,就会导致服务端被动关闭,若服务端不主动关闭 socket 发 FIN 给 Client,此时服务端 Socket 会处于 CLOSE_WAIT 状态(而不是 LAST_ACK 状态)。通常来说,一个 CLOSE_WAIT 会维持至少 2 个小时的时间(系统默认超时时间的是 7200 秒,也就是 2 小时)。如果服务端程序因某个原因导致系统造成一堆 CLOSE_WAIT 消耗资源,那么通常是等不到释放那一刻,系统就已崩溃。
先来介绍 tcp keepalive 的三个参数:
vi /etc/sysctl.conf
tcp_keepalive_time
tcp_keepalive_intvl
tcp_keepalive_probes
默认 tcp_keepalive_time 的值为 7200 ,超时时间(开启 keepalive 的闲置时长);
默认 tcp_keepalive_intvl 的值为 75 ,tcp 检查间隔时间(keepalive 探测包的发送间隔);
默认 tcp_keepalive_probes 的值为 9 ,tcp 检查次数(如果对方不予应答,探测包的发送次数);
根据自身的业务来调整这三个参数:
1800 15 3
600 60 5
600 2 2
保存退出并手动生效:
sysctl -p
其他配置:
#对于一个新建连接,内核要发送多少个 SYN 连接请求才决定放弃,不应该大于255,默认值是5,对应于180秒左右时间
net.ipv4.tcp_syn_retries=2
#net.ipv4.tcp_synack_retries=2
#表示当keepalive起用的时候,TCP发送keepalive消息的频度。缺省是2小时,改为300秒
net.ipv4.tcp_keepalive_time=1200
net.ipv4.tcp_orphan_retries=3
#表示如果套接字由本端要求关闭,这个参数决定了它保持在FIN-WAIT-2状态的时间
net.ipv4.tcp_fin_timeout=30
#表示SYN队列的长度,默认为1024,加大队列长度为8192,可以容纳更多等待连接的网络连接数。
net.ipv4.tcp_max_syn_backlog = 4096
#表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭
net.ipv4.tcp_syncookies = 1
#表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭
net.ipv4.tcp_tw_reuse = 1
#表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭
net.ipv4.tcp_tw_recycle = 1
##减少超时前的探测次数
net.ipv4.tcp_keepalive_probes=5
##优化网络设备接收队列
net.core.netdev_max_backlog=3000
Socket 关闭
Socket - 套接字,用于实现网络通信的类。
Java Socket提供了几种关闭方式,常用的有以下两种:
close()方法:该方法用于关闭Socket和相关的输入输出流,释放与Socket关联的资源。
shutdownOutput()方法:该方法用于关闭Socket的输出流,仅关闭输出流,输入流仍然可用。
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
public class SocketExample {
public static void main(String[] args) {
Socket socket = null;
try {
socket = new Socket("127.0.0.1", 8080);
OutputStream outputStream = socket.getOutputStream();
// 进行输出操作
outputStream.close();
socket.shutdownOutput();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
实时更新的数据可以使用长连接
长连接适用场景:
监控系统:后台硬件热插拔、LED、温度、电压发生变化;
即时通信系统:其它用户登录、发送信息;
即时报价系统:后台数据库内容发生变化;
像以上这些连接,如果每次操作都要建立连接然后再操作的话处理速度会降低。所以操作时第一次连接上以后,以后每次直接发送数据就可以了,不用再建立TCP连接。
再比如:数据库的连接用长连接,如果用短连接频繁的通信会造成socket错误,频繁的socket创建也是对资源的浪费。Comet
以前的http的长连接技术不成熟,目前有新技术名叫Comet:基于 HTTP 长连接的“服务器推”技术。
终极解决方法
实现负载均衡:通过实现负载均衡,将请求分发到多个服务器上,以分散负载并提高系统的吞吐量。负载均衡可以帮助避免单个服务器过载的情况发生。
调整应用程序逻辑:如果应用程序的逻辑不合理或存在性能瓶颈,可能会导致接口响应缓慢或超时。可以对应用程序逻辑进行调整或优化,以提高接口的响应速度和处理能力。
看着像啊,盲猜。负载均衡对外的链接数被限制了,比如port_range设置过小达不到65536这样。请求一直发,很容易就打满端口号,
这里提出两个思考题,我觉得非常有意义,大家自己思考下:
为什么一台机器几百个 CLOSE_WAIT 就导致不可继续访问?我们不是经常说一台机器有 65535 个文件描述符可用吗?
为什么我有负载均衡,而两台部署服务的机器确几乎同时出了 CLOSE_WAIT ?
看着像啊,盲猜。负载均衡对外的链接数被限制了,比如port_range设置过小达不到65536这样。请求一直发,很容易就打满端口号,
1.需要看最大连接数配置的是多少
2.业务问题导致的异常和负载均衡没关系,出问题肯定是都出问题
请问一下 ,socket 状态 监控工具用的是什么工具