背景
提供一个Socket服务,在普通的linux操作系统上运行全部没有问题(并线上正常支持万级运行几年了),但是在麒麟操作系统上高并发存在问题。普通linux操作系统都可以利用在/etc/sysctl.conf最后加上如下操作实现快速回收socket短连接释放的端口,TIME_WAIT是一个正常的业务处理完成,并通过下面的配置能够快速释放,除非单机需要上万并发。但是在麒麟操作系统下不存在net.ipv4.tcp_tw_recycle参数,即在大并发下TIME_WAIT无法实现快速回收,总共60000多个端口很快占用,导致无法提供后续服务。
#打开重用(主要)
net.ipv4.tcp_tw_reuse = 1
#打开快速回收(主要)
net.ipv4.tcp_tw_recycle = 1
处理方式1
服务端直接通过SocketChannel进行关闭,发现通过.setSoLinger(true, 0);能够快速关闭以及释放端口。
#当不是0是1时,是等待处理后再关闭。 但是只要是服务端主动关或者设置成1关闭都端口会出现TIME_WAIT。当为0时候使用java编写socket客户端调用,不存在问题,都能正常接收返回。不存在问题。并服务端不存在TIME_WAIT,客户端自己关闭会存在TIME_WAIT问题。
SocketChannel.socket().setSoLinger(true, 0);
问题:c写的客户端存在问题,可能是其中一种(1.数据没收全,被关了,2.自己关闭socket连接没处理各种情况) ,反正是有问题。
处理方式2
为了适配c写的客户端,只能等待接收客户端发送的关闭socket连接的信号-1,再自己执行SocketChannel.socket().setSoLinger(true, 0);
//比较傻的方式,会影响极少部分性能
SocketChannel sc = (SocketChannel) key.channel();
try {
SocketRequest request = (SocketRequest) key.attachment();
notifier.fireOnWrite(request);
writeData(sc, request.getResData());
ByteBuffer buffer =null;
//10秒
for (int i = 0; i < 10000; i++) {
buffer = ByteBuffer.allocate(1024);
int j = sc.read(buffer);
if (j == -1) {
sc.socket().setSoLinger(true, 0);
break;
} else {
sleep(1);
}
}
} catch (Exception e) {
log.error("发送数据时失败:" + e.getMessage());
e.printStackTrace();
} finally {
try {
notifier.fireOnClosed(sc);
sc.finishConnect();
sc.socket().close();
sc.close();
} catch (Exception x) {
log.error("关闭socket失败");
} finally {
try {
sc.close();
} catch (Exception e) {
log.error("关闭socket失败");
}
}
}
注意1
.setSoLinger(true, 0);需要如下配合使用才生效。
SocketChannel sc = (SocketChannel) key.channel();
sc.socket().setSoLinger(true, 0);
#执行下面两句话才关闭socket连接,将端口释放。
sc.socket().close();
sc.close();
注意2
如果由客户端管理连接,服务端没处理,会出现大量的CLOSE_WAIT,一样会导致服务端端口占满无法提供服务。一般是谁先关闭连接,谁会出现TIME_WAIT。
注意3
千万别用麒麟桌面端做服务器、千万别用麒麟桌面端做服务器、千万别用麒麟桌面端做服务器,重要的事说三遍。
全部处理好了最后单服务处理1000并发 2800吞吐量如下
单次测试网络抓包
最后服务端收到了[FIN,ACK]终止信号,后执行[RST,ACK]这可以立即终止当前连接,释放资源。
#网络通信说明
评论区