所以我们在后端使用 Spring websocket STOMP + RabbitMQ,我们在打开文件描述符时遇到了问题。一段时间后,我们达到了服务器的限制,服务器不接受任何连接,包括 websocket 和 API 端点。
2018-09-14 18:04:13.605 INFO 1288 --- [MessageBroker-1]
o.s.w.s.c.WebSocketMessageBrokerStats : WebSocketSession[2 current WS(2)-
HttpStream(0)-HttpPoll(0), 1159 total, 0 closed abnormally (0 connect
failure, 0 send limit, 63 transport error)], stompSubProtocol[processed
CONNECT(1014)-CONNECTED(1004)-DISCONNECT(0)], stompBrokerRelay[9 sessions,
127.0.0.1:61613 (available), processed CONNECT(1015)-CONNECTED(1005)-
DISCONNECT(1011)], inboundChannel[pool size = 2, active threads = 2, queued
tasks = 2, completed tasks = 12287], outboundChannelpool size = 0, active
threads = 0, queued tasks = 0, completed tasks = 4225], sockJsScheduler[pool
size = 1, active threads = 1, queued tasks = 3, completed tasks = 683]
我们得到以下例外:
2018-09-14 18:04:13.761 ERROR 1288 --- [http-nio-127.0.0.1-8443-Acceptor-0]
org.apache.tomcat.util.net.NioEndpoint : Socket accept failed
java.io.IOException: Too many open files
at sun.nio.ch.ServerSocketChannelImpl.accept0(Native Method)
at sun.nio.ch.ServerSocketChannelImpl.accept(ServerSocketChannelImpl.java:422)
at sun.nio.ch.ServerSocketChannelImpl.accept(ServerSocketChannelImpl.java:250)
at org.apache.tomcat.util.net.NioEndpoint$Acceptor.run(NioEndpoint.java:455)
at java.lang.Thread.run(Thread.java:748)
linux 的默认文件描述符限制是 1024,即使我们将其增加到 65000 之类的东西,无论如何它都会在某个时候达到限制。
我们想从后端解决这个问题,最好是通过 Spring 而没有任何变通方法。有任何想法吗?
更新
RabbitMQ 和应用程序驻留在不同的服务器上。实际上,RabbitMQ 在 Compose 上工作。我们可以通过不从客户端发送DISCONNECT消息来重现此问题。
更新 2
今天我意识到,无论发生什么,所有的文件描述符和 java 线程总是留在那里。我已经实现了一种解决方法,包括从 Spring 发送DISCONNECT消息并关闭WebSocketSession
对象并且不进行任何更改。我通过检查以下链接实现了这些:
- 从 Spring websocket stomp 服务器断开客户端会话
- https://github.com/isaranchuk/spring-websocket-disconnect
- https://github.com/rstoyanchev/spring-websocket-portfolio
作为旁注,服务器端发送这样的消息:
simpMessagingTemplate.convertAndSend("/queue/" + sessionId, payload)
. 这样,我们就保证了每个客户端都得到了相关的消息sessionId.
这是某种错误吗?为什么不关闭文件描述符?以前没有人遇到过这个问题吗?
更新 3
每次关闭套接字时,我都会看到以下异常。无论是通过来自客户端的DISCONNECT消息还是webSocketSession.close()
来自服务器的代码,它是如何关闭的都没有关系。
[reactor-tcp-io-66] o.s.m.s.s.StompBrokerRelayMessageHandler : TCP connection failure in session 45r7i9u3: Transport failure: epoll_ctl(..) failed: No such file or directory
io.netty.channel.unix.Errors$NativeIoException: epoll_ctl(..) failed: No such file or directory
at io.netty.channel.unix.Errors.newIOException(Errors.java:122)
at io.netty.channel.epoll.Native.epollCtlMod(Native.java:134)
at io.netty.channel.epoll.EpollEventLoop.modify(EpollEventLoop.java:186)
at io.netty.channel.epoll.AbstractEpollChannel.modifyEvents(AbstractEpollChannel.java:272)
at io.netty.channel.epoll.AbstractEpollChannel.clearFlag(AbstractEpollChannel.java:125)
at io.netty.channel.epoll.AbstractEpollChannel$AbstractEpollUnsafe.clearEpollRdHup(AbstractEpollChannel.java:450)
at io.netty.channel.epoll.AbstractEpollChannel$AbstractEpollUnsafe.epollRdHupReady(AbstractEpollChannel.java:442)
at io.netty.channel.epoll.EpollEventLoop.processReady(EpollEventLoop.java:417)
at io.netty.channel.epoll.EpollEventLoop.run(EpollEventLoop.java:310)
at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:884)
at java.lang.Thread.run(Thread.java:748)
所以我将日志级别更改为TRACE
,我看到 websocket 确实被关闭了,但是这些异常立即被抛出。所以在这一点上,我真的很怀疑这个例外。挂起的 java 线程的数量总是与 websocket 的数量齐头并进,即创建 400 个 websocket 总是在主进程中最终导致 ~400 个挂起的线程。并且内存资源永远不会被释放。
谷歌搜索此异常仅得到以下 4 个结果:(其余为其他异常)
- https://github.com/netty/netty/issues/2414
- https://github.com/reactor/reactor-ipc/issues/16
- https://github.com/cloudfoundry/cf-java-client/issues/495
- https://github.com/cloudfoundry/cf-java-client/issues/480
将库更新netty
到最新版本(4.1.29.Final)也不起作用,所以我相应地更改了问题的标签。我也在考虑针对netty
. 我已经尝试了很多东西,并在应用程序级别上进行了多次实验,但似乎没有任何效果。在这一点上,我对任何想法都持开放态度。