侧边栏壁纸
博主头像
会飞的大象博主等级

爱运动的程序猿

  • 累计撰写 126 篇文章
  • 累计创建 158 个标签
  • 累计收到 0 条评论
标签搜索

目 录CONTENT

文章目录

Socket服务端大量TIME_WAIT问题

会飞的大象
2024-10-24 / 0 评论 / 0 点赞 / 421 阅读 / 864 字

背景

提供一个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吞吐量如下

image
image-1729754233545
image-1729754239867

单次测试网络抓包

最后服务端收到了[FIN,ACK]终止信号,后执行[RST,ACK]这可以立即终止当前连接,释放资源。
image-1729824114757
#网络通信说明
image-1729824353255

0

评论区