netty运行一段时间报错:java.io.IOException: 打开的文件过多

报错详细内容如下:

java.io.IOException: 打开的文件过多
	at sun.nio.ch.ServerSocketChannelImpl.accept0(Native Method)
	at sun.nio.ch.ServerSocketChannelImpl.accept(ServerSocketChannelImpl.java:421)
	at sun.nio.ch.ServerSocketChannelImpl.accept(ServerSocketChannelImpl.java:249)
	at io.netty.util.internal.SocketUtils$5.run(SocketUtils.java:119)
	at io.netty.util.internal.SocketUtils$5.run(SocketUtils.java:116)
	at java.security.AccessController.doPrivileged(Native Method)
	at io.netty.util.internal.SocketUtils.accept(SocketUtils.java:116)
	at io.netty.channel.socket.nio.NioServerSocketChannel.doReadMessages(NioServerSocketChannel.java:147)
	at io.netty.channel.nio.AbstractNioMessageChannel$NioMessageUnsafe.read(AbstractNioMessageChannel.java:75)
	at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:719)
	at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:655)
	at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:581)
	at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:493)
	at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:989)
	at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
	at java.lang.Thread.run(Thread.java:748)

问题背景

使用netty做了一个TCP/IP的通讯服务,我们作为TCP的服务端接收客户端发送的请求,项目部署在Linux环境下运行。但是运行一段时间,服务就崩了,报错内容如上:java.io.IOException: 打开的文件过多 或 java.io.IOException: Too many open files。反正意思一样,都是当前服务文件句柄数超出上限,内存达到上限,服务承受不住了。这个时候把netty服务重启一下就又恢复了…循环往复,苦不堪言

一般出现这类问题,都是在网上一通搜索,基本的方案不外乎以下几种:

🚀方案一,升级netty的版本
结果:没用

版本升级之后,运行一段时间又成这样了,肯定不是版本的问题了

🚀方案二,初始化NioEventLoopGroup的时候,传参限制1个

EventLoopGroup bossGroup = new NioEventLoopGroup(1);

结果:没用

网上有些人真的是误人子弟,你抄我、我抄你的,本来实例化的也是1个对象,这样改有什么意义呢?

🚀方案三,修改/etc/security/limits.conf中的数量限制,默认是1024,改为65536

* soft nofile 65536
* hard nofile 65536

结果:短时间有用,运行几天之后又报这个错了

典型的治标不治本。这种情况针对的是并发很高,TCP客户端超过1024了,但是我们总共就100多个客户端,怎么会超出呢。改了这个只把句柄数量上限提高了,根本上还是没有解决问题,过几天还是要崩的!

 
💔行了,网上的解决方案都没用,只能靠自己分析报错原因了。客户端总共就100多个,怎么过几天就超过65536了呢?肯定是单个客户端建立了多次连接,但是服务端的连接没有及时关闭所致。正常客户端会主动断开连接并重新建立连接,而这个项目的客户端可能就没有做断开操作,而是不断向服务端建立新的连接,所以我们要在服务端做好处理了。

通过三条命令,更加佐证了我的想法:

lsof -n|awk '{print $2}'|sort|uniq -c|sort -nr|more

查询服务倒序连接数,可以看到linux下所以服务进程号对应的连接数量,从高到低排序


netstat -lnaop|grep 5655|wc -l

查询某进程号连接数,此时我的netty服务对应的进程号是5655,间隔时间多查几次,发现连接数量在不断递增,早就超过了预留客户端的数量


netstat -ntu | grep 10072

查询某端口客户端连接的情况,此时我netty服务开放的TCP端口是10072,列表查到占用资源的客户端列表,发现大量重复的客户端IP地址


👍 重点来了,解决根本问题

最终解决方案:netty初始化的时候,设置连接超时时间 pipeline.addLast(new ReadTimeoutHandler(timeoutSeconds))

/**
 * 设置初始化器,主要是给pipeLine添加Handler
 */
public class MyServerInitializer  extends ChannelInitializer<SocketChannel> {

    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        
        //设置2min的超时时间,如果某个通道2min内未发送信号,则抛出异常删除当前通道
        pipeline.addLast(new ReadTimeoutHandler(120));
        
        pipeline.addLast("encoder",new StringEncoder(CharsetUtil.UTF_8));
        pipeline.addLast("decoder",new StringDecoder(CharsetUtil.UTF_8));
        pipeline.addLast(new MyServerHandler());
    }

}

        我这边设置的是2分钟,具体按照项目的实际需求设置,因为我这边的客户端是每1分钟发送一次报文的,我就暂时设置为2分钟了。超过2分钟没发数据,服务端就认为这个连接超时了,超时就会在执行到exceptionCaught方法中并且断开当前连接。我们的MyServerHandler类继承了ChannelInboundHandlerAdapter类或SimpleChannelInboundHandler类,自然而然就重写了exceptionCaught这个方法。而netty框架中某通道发生异常的时候,会在执行到exceptionCaught方法中。
 
        这样我们就完美解决了这个问题,间隔时间多跑几次【netstat -lnaop|grep 5655|wc -l】命令,会发现连接数稳定在100多个了!
 
在这里插入图片描述

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值