欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 教育 > 锐评 > 【java】 Too many open files

【java】 Too many open files

2025/5/15 16:20:10 来源:https://blog.csdn.net/weixin_66907051/article/details/138618279  浏览:    关键词:【java】 Too many open files

Too many open files

背景

在进行了大量push消息推送后,回执接口出现了大量 Too many open files异常信息

解决

出现这个错误,表示系统文件描述符已经被耗尽,新的请求无法使用文件描述符号

  1. 查询系统配置的最大文件描述符配置
    ulimit -a 
    
  2. 查询内核分配的最大描述符
    cat /proc/sys/fs/file-max
    
  3. 查询进程分配的最大描述符, 不通机器配置不一样,会出现进程配置很小的情况
    // PID 当前进程ID
    cat /proc/pid/limits
    

在查询配置后,发现上述三者都配置的为65535;因为项目整体并发量很大,初步怀疑是否因为并发量很高导致出现的问题,但查询日志后发现,初次出现Too many open files后已经经过了10个小时,但是错误任然持续出现;并且在晚上推送完工单后,我们并没有经过其他工单消息的推送,数据回执率已达到79%, 接口请求数也很少,已经可以排除是并发量太大的问题

尬尴的是,因为服务器,还有其他服务,此时也受到了影响,怕影响其他业务使用,同事让运维重启了服务, 破坏了犯罪现场 , 推送服务重启后,其他服务正常运行

犯罪现场已经被破坏,无法查询到在重启之前服务使用了多少文件描述符, 因为已经排除了并发量大导致出现问题的可能,在重启推送服务后,其他服务也可以正常运行,所以怀疑是文件描述符泄漏导致的问题

项目中常见文件描述符泄漏出现的场景
  1. 文件操作时,没有正确的关闭文件流

    在java中文件流不关闭,并不会导致文件描述符被长期持有,Java 的垃圾回收机制和输入输出流类的 finalize() 方法会自动释放文件的描述符.

  2. 创建大量socket客户端,没有正确关闭 socket;

    java的垃圾回收不能关闭网络连接打开的文件句柄,如果没有执行close()(例如:java.net.Socket.close())则文件句柄将一直存在,而不能被关闭

项目中并没有文件相关操作,所以只可能时socket客户端的问题,并且项目中的确存在使用的socket的情况 , 以下是项目重启后的句柄的持有情况(使用上述第三条命令)

java    600172 middle   76u     IPv6         3485548397        0t0        TCP a04-hhht-ggpt-tomcat09:22753->10.35.2.68:27017 (ESTABLISHED)
java    600172 middle   77u     IPv6         3485633586        0t0        TCP a04-hhht-ggpt-tomcat09:22751->10.35.2.68:27017 (ESTABLISHED)
java    600172 middle   78u  a_inode               0,10          0       7510 [eventpoll]
java    600172 middle   79u  a_inode               0,10          0       7510 [eventfd]
java    600172 middle   80u  a_inode               0,10          0       7510 [eventpoll]
java    600172 middle   81u  a_inode               0,10          0       7510 [eventfd]
java    600172 middle   82u  a_inode               0,10          0       7510 [eventpoll]
java    600172 middle   83u  a_inode               0,10          0       7510 [eventfd]
java    600172 middle   84u  a_inode               0,10          0       7510 [eventpoll]
java    600172 middle   85u  a_inode               0,10          0       7510 [eventfd]
java    600172 middle   86u  a_inode               0,10          0       7510 [eventpoll]
java    600172 middle   87u  a_inode               0,10          0       7510 [eventfd]
java    600172 middle   88u  a_inode               0,10          0       7510 [eventpoll]
java    600172 middle   89u  a_inode               0,10          0       7510 [eventfd]
java    600172 middle   90u  a_inode               0,10          0       7510 [eventpoll]
java    600172 middle   91u  a_inode               0,10          0       7510 [eventfd]
java    600172 middle   92u  a_inode               0,10          0       7510 [eventpoll]
java    600172 middle   93u  a_inode               0,10          0       7510 [eventfd]
java    600172 middle   94u  a_inode               0,10          0       7510 [eventpoll]
java    600172 middle   95u  a_inode               0,10          0       7510 [eventfd]
java    600172 middle   96u  a_inode               0,10          0       7510 [eventpoll]
java    600172 middle   97u  a_inode               0,10          0       7510 [eventfd]
java    600172 middle   98u  a_inode               0,10          0       7510 [eventpoll]
java    600172 middle   99u  a_inode               0,10          0       7510 [eventfd]
java    600172 middle  100u  a_inode               0,10          0       7510 [eventpoll]
java    600172 middle  101u  a_inode               0,10          0       7510 [eventfd]
java    600172 middle  102u  a_inode               0,10          0       7510 [eventpoll]
java    600172 middle  103u  a_inode               0,10          0       7510 [eventfd]
java    600172 middle  104u  a_inode               0,10          0       7510 [eventpoll]
java    600172 middle  105u  a_inode               0,10          0       7510 [eventfd]
java    600172 middle  106u  a_inode               0,10          0       7510 [eventpoll]
java    600172 middle  107u  a_inode               0,10          0       7510 [eventfd]
java    600172 middle  108u  a_inode               0,10          0       7510 [eventpoll]
java    600172 middle  109u  a_inode               0,10          0       7510 [eventfd]
java    600172 middle  110u  a_inode               0,10          0       7510 [eventpoll]
java    600172 middle  111u  a_inode               0,10          0       7510 [eventfd]
java    600172 middle  112u  a_inode               0,10          0       7510 [eventpoll]
java    600172 middle  113u  a_inode               0,10          0       7510 [eventfd]

从上图可以看到,有很多关于eventpolleventfd相关的句柄;eventpolleventfd都是 Linux 系统中用于事件通知和处理的机制。

eventpoll

eventpoll是 Linux 中的一个 I/O 事件通知接口,通常用于实现高效的事件驱动的 I/O。它允许进程监视多个文件描述符,以便在它们中的任何一个上发生事件时得到通知。
通过 epoll_create创建一个 epoll实例,然后使用 epoll_ctl来注册文件描述符和事件,最后使用 epoll_wait来等待事件的发生。

eventpoll提供了一种高效的 I/O 多路复用机制,可以用于实现高性能的网络服务器和其他事件驱动的应用程序。

eventfd

eventfd是一个用于在用户空间应用程序之间传递事件的文件描述符。它提供了一种简单的事件通知机制,可以通过文件描述符传递事件信号。

应用程序可以创建一个 eventfd对象,并将其文件描述符传递给其他应用程序。当某个应用程序向 eventfd写入数据时,另一个应用程序可以通过监听该 eventfd文件描述符来获取事件通知。

最终,在排查完项目中所有socket相关操作后发现,发现在使用苹果push消息推送的时,在出现推送异常后,虽然有进行错误异常捕获,但是没有执行客户端的close方法,导致大量的文件句柄被占用

 public boolean pushMsgByPushy(AppInfo appInfo, Message msg)throws IOException, InterruptedException {int deployStats = appInfo.getDeployStat();// 认证证书字节数组byte[] bytes = appInfo.getApnsCert();String password = appInfo.getDptApnsSecret();// 密钥String hostname = ApnsClientBuilder.DEVELOPMENT_APNS_HOST;String payload = getAsIosPayload(msg);// 设备IDfinal String token = TokenUtil.sanitizeTokenString(msg.getDeviceId());final ApnsClient apnsClient = new ApnsClientBuilder().setApnsServer(hostname).setClientCredentials(new ByteArrayInputStream(bytes), password).build();final String topic = "api.push.apple.11235";final SimpleApnsPushNotification pushNotification = new SimpleApnsPushNotification(token, topic, payload);final PushNotificationFuture<SimpleApnsPushNotification, PushNotificationResponse<SimpleApnsPushNotification>> sendNotificationFuture = apnsClient.sendNotification(pushNotification);try {final PushNotificationResponse<SimpleApnsPushNotification> pushNotificationResponse = sendNotificationFuture.get();if (!pushNotificationResponse.isAccepted()) {pushNotificationResponse.getTokenInvalidationTimestamp().ifPresent(timestamp -> log.error("the token is invalid as of timestamp={}", timestamp));}} catch (final ExecutionException e) {log.error("failed to send push notification. msg={}", msg, e);// 问题出现在这里, 发生异常后直接进行了返回, 没有关闭 ApnsClient, 苹果推送底层使用自建socket方式 return result.get();}sendNotificationFuture.whenComplete((response, cause) -> {//推送完成后业务处理 });//释放系统资源final CompletableFuture<Void> closeFuture = apnsClient.close();return result.get();}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词