一、io.netty.channel.unix.Errors$NativeIoException: accept(..) failed: 打开的文件过多
1.问题
1804012290 [reactor-http-epoll-1] WARN i.n.channel.DefaultChannelPipeline - An exceptionCaught() event was fired, and it reached at the tail of the pipeline. It usually means the last handler in the pipeline did not handle the exception. -
io.netty.channel.unix.Errors$NativeIoException: accept(..) failed: 打开的文件过多
2024-11-27 14:25:52.849 WARN 40110 --- [or-http-epoll-1] io.netty.channel.DefaultChannelPipeline : An exceptionCaught() event was fired, and it reached at the tail of the pipeline. It usually means the last handler in the pipeline did not handle the exception.
2.查看linux默认连接数
ulimit -n
如果要查看是哪个进程打开的较多,可以使用
#使用ps -ef |grep java
#77281 表示进程id
lsof -p 77281 |wc -l
如果想查看 系统的一些限制参数,可以使用下面的
如果想查看当前系统设置的最大句柄数是多少,可以使用
ulimit -a
3.修改允许打开的文件数
3.1 修改允许打开的文件数——命令方式
这种设置方法是临时修改,在重启后会还原为默认值
3.1.1 vi /etc/profile
在文件末尾加入
#wx add 20241205
ulimit -n 65535
3.1.2 使其生效
source /etc/profile
3.2 修改允许打开的文件数——修改系统配置文件
这种设置方法会永久有效
vim /etc/security/limits.conf
#在最后加入
* soft nofile 65535
* hard nofile 65535
或
* - nofile 65535
最前的 * 表示所有用户,可根据需要设置某一用户,例如
wux soft nofile 65535
wux hard nofile 65535
注意”nofile”项有两个可能的限制措施。就是项下的hard和soft。 要使修改过得最大打开文件数生效,必须对这两种限制进行设定。 如果使用"-"字符设定, 则hard和soft设定会同时被设定.
几个关于文件描述符的命令:
当前系统允许创建的最大文件描述符的数量
cat /proc/sys/fs/file-max
当前会话session的允许创建的最大文件描述符,默认每个进程允许打开的最大文件描述符数量应该是1024
ulimit -n
统计当前所有进程的占用的文件描述符的总量
lsof | wc -l
查看每个进程打开的文件描述符的数量,并按打开的数量降序排序:
lsof -n |awk '{print $2}'|sort|uniq -c |sort -nr
PS:lsof命令默认系统是没有安装的,需要执行下面的命令安装,此外这个命令需要用root执行:
yum install -y lsof
二、Socket accept failed java.io.IOException: Too many open files
在开发网络应用程序时,经常会遇到一些异常情况。例如java.io.IOException: Too many open files
,这是由于系统限制了可同时打开的文件描述符数量而引起的。
问题的原因
在操作系统中,每个进程都有一个打开文件的限制。默认情况下,Linux和Windows系统的文件描述符限制分别为1024和2048。当应用程序打开的文件描述符数量超过限制时,系统将抛出java.io.IOException: Too many open files
异常。
在网络编程中,当我们使用Socket
进行通信时,每个Socket
都会占用一个文件描述符。如果我们在处理连接时没有正确关闭Socket
,或者并发连接数过多,就容易超过系统的文件描述符限制。
解决方案
要解决这个问题,我们需要采取以下几个步骤:
1. 检查系统文件描述符限制
首先,我们需要检查系统的文件描述符限制。我们可以使用ulimit -n
命令来查看当前限制的大小。如果限制值太小,我们可以使用ulimit -n <new_limit>
命令将其增加到一个较大的值。
2. 优化代码中的资源使用
接下来,我们需要检查代码中是否有资源没有正确关闭。确保在使用完Socket
后,调用close()
方法来关闭它。在使用try-with-resources
语句块时,可以自动关闭资源。
try (Socket socket = new Socket()) {
// 使用Socket进行通信
} catch (IOException e) {
// 处理异常
}
3. 实现连接池
如果我们的应用程序需要同时处理大量的并发连接,那么使用连接池可以有效地管理和重用Socket
资源。连接池可以限制并管理连接的数量,确保不会超出系统的文件描述符限制。
我们可以使用第三方库,如Apache Commons Pool,来实现连接池功能。下面是一个简单的示例代码,演示如何使用Apache Commons Pool实现连接池:
复制
GenericObjectPoolConfig<Socket> poolConfig = new GenericObjectPoolConfig<>();
poolConfig.setMaxTotal(100); // 设置最大连接数
poolConfig.setBlockWhenExhausted(true); // 当连接耗尽时,是否阻塞等待
poolConfig.setMaxWaitMillis(5000); // 最大等待时间(毫秒)
SocketFactory factory = new SocketFactory() {
@Override
public Socket create() throws Exception {
return new Socket("localhost", 8080);
}
@Override
public PooledObject<Socket> wrap(Socket obj) {
return new DefaultPooledObject<>(obj);
}
};
try (GenericObjectPool<Socket> pool = new GenericObjectPool<>(factory, poolConfig)) {
Socket socket = pool.borrowObject(); // 从连接池中获取连接
// 使用Socket进行通信
pool.returnObject(socket); // 将连接返回到连接池
} catch (IOException e) {
// 处理异常
}