Поэтому мы используем веб-сокет Spring STOMP + RabbitMQ на бэкэнде, и у нас возникают проблемы с дескрипторами открытых файлов. Через определенное время мы достигли предела на сервере, и сервер не принимает никаких подключений, включая как веб-сокеты, так и конечные точки 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-потоки всегда остаются там, что бы ни случилось. Я реализовал обходной путь, который включает отправку сообщений DISCONNECT из Spring и закрытие объектов 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
и вижу, что вебсокеты действительно закрываются, но тут же кидаются эти исключения. Так что на данный момент я очень подозрительно отношусь к этому исключению. Количество зависших потоков Java всегда идет рука об руку с количеством веб-сокетов, т.е. создание 400 веб-сокетов всегда приводит к ~400 зависшим потокам в основном процессе. И ресурсы памяти никогда не освобождаются.
Поиск в Google этого исключения приводит только к 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
. Я пробовал много вещей и несколько раз экспериментировал на уровне приложения, но ничего не работает. На данный момент я открыт для любых идей.
spring-amqp
. - person Karol Dowbecki   schedule 24.09.2018spring-boot-starter-websocket
, и я думаю, что она не включаетspring-amqp
. В моем случае проблема заключается не в том, что потоки Java создаются немедленно, а в том, что они не закрываются. Возможно, мне следует переключить версию на последнюю и повторить попытку. - person leventunver   schedule 24.09.2018