提问者:小点点

通过Spring Websocket STOMP打开连接导致我们的服务器死亡


所以我们在后端使用Spring websocket STOMP RabbitMQ,打开的文件描述符有问题。一段时间后,我们达到了服务器的限制,服务器不接受任何连接,包括websocket和APIendpoint。

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适用于撰写。我们可以通过不从客户端发送DISCONNECT消息来重现此问题。

更新2

今天我意识到所有的文件描述符和java线程总是停留在那里,不管发生什么。我已经实现了一个解决方法,包括从Spring发送DISCONNECT消息并关闭WebSocketSession对象并且没有更改。我通过检查以下链接实现了这些:

  • 断开客户端会话与Spring websocket踩踏服务器的连接
  • https://github.com/isaranchuk/spring-websocket-disconnect
  • https://github.com/rstoyanchev/spring-websocket-portfolio

附带说明一下,服务器端发送的消息是这样的:simpMessagingTemplace.转换和发送("/que/"session sionId, payload)。这样,我们确保每个客户端都通过相关的session sionId获得相应的消息。

这是某种bug吗?为什么文件描述符没有关闭?以前没有人遇到过这个问题吗?

更新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创建一个问题。我在应用程序级别上尝试了很多东西并尝试了几次,但似乎没有什么效果。在这一点上,我对任何类型的想法都持开放态度。


共2个答案

匿名用户

如果您总是使用try-with资源或在最后块中关闭打开的文件,您可能真的超出了文件描述符限制,并且需要另一台主机来接受您的请求。为此,您需要扩展您的应用程序并对其进行负载平衡。我建议您在集群中部署Rabbitmq以解决此问题。

在某些情况下,RabbitMQ会忽略您的文件描述符限制。

匿名用户

RabbitMQJava客户端库有时会在管理打开的文件描述符方面遇到问题。这很少是坏的,但也有问题,例如ChannelManager第218行。

你想尝试几个不同的Java客户端库版本,因为这是一个客户端问题。在一个版本中,由于连接创建错误,我产生了数千个Java线程(不确定哪个版本受到影响,我通过使用Flight Recorder并转到锁部分发现了这一点,所有线程都在等待获取RabbitMQ连接(?)类锁)。