java.io.IOException: Too many open files 分析与解决

一 问题背景:

        近日有个运营了几个月的新项目,客户方告知充电桩设备断开后无法再重连接服务器,之前是的正常,于是检查 接入的前置服务器项目日记,发现项目日记最近几天在量报错java.io.IOException: Too many open files ,如下图:

项目日记时不时会报错 

二 分析具体原因:

        开发网络应用时遇到 java.io.IOException: Too many open files 错误,这意味着你的应用程序尝试打开的文件描述符数量超过了系统允许的最大数量。在 Linux 中,每个进程可以打开的文件描述符的数量是有限制的。 

1 文件描述符列表:

  1 标准文件描述符
  • stdin:标准输入,默认指向键盘。
  • stdout:标准输出,默认指向终端。
  • stderr:标准错误输出,默认指向终端。
2 普通文件
  • 打开的普通文件,如文本文件、二进制文件等。
3 目录
  • 打开的目录。
4 设备文件
  • 如块设备文件(block devices)和字符设备文件(character devices)。
5 网络套接字(重点):
  • 包括 TCP、UDP 等类型的套接字
  • 例如,一个 HTTP 服务器可能会打开多个 TCP 套接字来处理客户端请求。
6 命名管道(FIFO)
  • 用于进程间通信的一种机制。
7 匿名管道
  • 用于父子进程间的通信。
8 共享内存对象
  • 用于进程间共享数据。
9 信号量
  • 用于进程间同步。
10 定时器
  • 用于实现定时功能。
11 事件文件描述符
  • 如 inotify 文件描述符,用于监视文件系统事件。
12 Unix 域套接字
  • 用于同一主机上的进程间通信

2 查看进程打开的文件描述符数量

 使用以下命令来查看某个 Java 进程当前打开的文件描述符数量:

1 找到 Java 进程的 PID

  你可以使用 ps aux | grep java 来查找进程的 PID。

ps aux | grep 应用名称
2 查看打开的文件描述符

  找到进程的 PID 后,可以使用 lsof 命令来查看该进程打开的文件描述符数量:

lsof -p <PID> | wc -l

 lsof 指令在统计比较多的网络符时会非常慢,可以改用以下指令:

ls -1 /proc/进程id号/fd | wc -l

这将返回一个近似的文件描述符数量,通常这个方法比 lsof 更快。 

 操作结果 如下图:

注意:由于当时急着修改问题没有截图,修复后补的图,如果是修复前应该不超系统限制的1024。

3 详细分析原因:

         当前项目是用来接收充电桩报文的前置服务器,每台充电桩占用一个TCP连接,即1个连接也就是1个文件描述符,项目上线已有几个月,随着业务的增加,充电桩的数量也相应增加,当前的充电桩从原有的100多台慢慢增加到一千多台,也就是说单单连接就占用1000多个文件描述符,加上就用本身的一些如进程通信定时器,文件日记等等的文件描述符,也就超过1024,所以产生了些错误。在修复后,为了明确应用占用了多少文件符,我们再次查看该应用发现有1627个文件描述符。

三 解决方案:

1 增加限制

  如果需要临时增加限制,可以在命令行中执行:

1ulimit -n 65536

  执行后如下图:

2 永久修改限制

  若要永久性地改变文件描述符限制,需要编辑 /etc/security/limits.conf 文件,添加如下行:

1* soft nofile 65536  
2* hard nofile 65536

        这里的 * 表示所有用户,也可以替换为特定用户名或用户组,如替换成root管理员。softhard 分别代表了可以由用户更改的限制和管理员才能更改的上限。

编辑 /etc/security/limits.conf 文件操作如下图:

3 查看当前限制

 可以通过下面的命令来查看当前用户的文件描述符限制:

1ulimit -n

 操作后如下图:

小结:

        在linux系统中一般涉及网络多设备接入基本都要配置系统的打开文件符号数,当设备连接超过600时,默认的1024是可能不够用的,不要忘记要养成好习惯。

在C#中遇到 "IOException: 文件正在被另一个进程使用" 的异常时,通常意味着你需要处理文件的并发访问问题。这种错误通常是由于两个或更多的线程或进程试图同时读写同一个文件,导致了文件锁定。以下是几种常见的处理方法: 1. **文件锁**:你可以使用 `FileStream` 类的 `FileShare` 构造函数和 `LockFile` 方法来获取共享锁(`FileShare.ReadWrite` 或 `FileShare.Write`),这会在打开文件时防止其他线程对文件进行写操作。确保在完成操作后释放锁。 ```csharp using (FileStream stream = new FileStream("c:\\20240601.txt", FileMode.Open, FileAccess.Write, FileShare.Read)) { // 执行需要写的操作 } ``` 2. **异步处理**:如果可能,尽量使用异步 I/O,比如 `File.OpenAsync` 和 `await Task.Delay` 来避免阻塞,让其他线程有机会访问文件。 3. **定时重试机制**:有时文件可能会暂时被锁定,可以设置一个超时机制,如使用 `Try/Catch` 结构,并在捕获到 IOException 后稍等一段时间再尝试。 ```csharp int retryCount = 0; while (retryCount < maxRetries) { try { using (FileStream stream = new FileStream("c:\\20240601.txt", FileMode.Open, FileAccess.Write, FileShare.Read)) { // 执行操作... break; // 如果成功就退出循环 } } catch (IOException e) when (retryCount++ < maxRetries) { System.Threading.Thread.Sleep(retryIntervalMilliseconds); } } ``` 4. **使用并行流或并发工具**:对于大量数据或复杂操作,可以考虑使用 `ParallelStreamReader` 或 `Task Parallel Library (TPL)` 中的并发方法来处理文件读取。 记得根据你的实际需求选择合适的策略,并始终注意资源管理,避免无谓的争抢造成系统性能下降。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qyhua

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值