SocketTimeout с открытым соединением в MongoDB

У меня есть приложение Java, которое выполняет некоторые агрегации в MongoDB, но иногда оно просто зависает и генерирует исключение SocketTimeout. После исключения приложение будет работать нормально (некоторое время, затем оно, вероятно, снова вызовет исключение).

Я только что нашел это объяснение, которое кажется возможной причиной, но я не уверен .

Я инициализирую MongoClient и оставляю соединение с БД открытым. Я не уверен, может ли это быть проблемой, и я должен просто каждый раз получать базу данных, а затем позволять базе данных собирать мусор (и закрывать соединение).

Другим подходом может быть периодический пинг Mongo, чтобы поддерживать «свежесть» пула соединений.

Используемый клиент выглядит примерно так:

public class DbClient {

    private static MongoClient mongoClient;
    private static MongoDatabase db;

    private DbClient() {}

    public static void init() throws Exception {
        mongoClient = new MongoClient();
    }

    public static MongoDatabase getDB() {
        if(mongoClient == null)
            throw new IllegalStateException("Client not initialized!");

        if(db == null) {
            db = mongoClient.getDatabase("my_db");
        }
        return db;
    }
}

Это возможная причина SocketTimeout?

Это исключение:

09:20:45.742 [qtp605535417-46] INFO  org.mongodb.driver.connection - Closed connection [connectionId{localValue:16, serverValue:6562}] to myapp.com:27017 because there was a socket exception raised by this connection.
09:20:45.743 [qtp605535417-46] ERROR myapp.service.Api - Error processing request
com.mongodb.MongoSocketReadTimeoutException: Timeout while receiving message
    at com.mongodb.connection.InternalStreamConnection.translateReadException(InternalStreamConnection.java:474) ~[mongo-java-driver-3.2.2.jar!/:na]
    at com.mongodb.connection.InternalStreamConnection.receiveMessage(InternalStreamConnection.java:225) ~[mongo-java-driver-3.2.2.jar!/:na]
    at com.mongodb.connection.UsageTrackingInternalConnection.receiveMessage(UsageTrackingInternalConnection.java:102) ~[mongo-java-driver-3.2.2.jar!/:na]
    at com.mongodb.connection.DefaultConnectionPool$PooledConnection.receiveMessage(DefaultConnectionPool.java:435) ~[mongo-java-driver-3.2.2.jar!/:na]
    at com.mongodb.connection.CommandProtocol.execute(CommandProtocol.java:112) ~[mongo-java-driver-3.2.2.jar!/:na]
    at com.mongodb.connection.DefaultServer$DefaultServerProtocolExecutor.execute(DefaultServer.java:159) ~[mongo-java-driver-3.2.2.jar!/:na]
    at com.mongodb.connection.DefaultServerConnection.executeProtocol(DefaultServerConnection.java:286) ~[mongo-java-driver-3.2.2.jar!/:na]
    at com.mongodb.connection.DefaultServerConnection.command(DefaultServerConnection.java:173) ~[mongo-java-driver-3.2.2.jar!/:na]
    at com.mongodb.operation.CommandOperationHelper.executeWrappedCommandProtocol(CommandOperationHelper.java:215) ~[mongo-java-driver-3.2.2.jar!/:na]
    at com.mongodb.operation.CommandOperationHelper.executeWrappedCommandProtocol(CommandOperationHelper.java:206) ~[mongo-java-driver-3.2.2.jar!/:na]
    at com.mongodb.operation.CommandOperationHelper.executeWrappedCommandProtocol(CommandOperationHelper.java:112) ~[mongo-java-driver-3.2.2.jar!/:na]
    at com.mongodb.operation.FindOperation$1.call(FindOperation.java:487) ~[mongo-java-driver-3.2.2.jar!/:na]
    at com.mongodb.operation.FindOperation$1.call(FindOperation.java:482) ~[mongo-java-driver-3.2.2.jar!/:na]
    at com.mongodb.operation.OperationHelper.withConnectionSource(OperationHelper.java:239) ~[mongo-java-driver-3.2.2.jar!/:na]
    at com.mongodb.operation.OperationHelper.withConnection(OperationHelper.java:212) ~[mongo-java-driver-3.2.2.jar!/:na]
    at com.mongodb.operation.FindOperation.execute(FindOperation.java:482) ~[mongo-java-driver-3.2.2.jar!/:na]
    at com.mongodb.operation.FindOperation.execute(FindOperation.java:79) ~[mongo-java-driver-3.2.2.jar!/:na]
    at com.mongodb.Mongo.execute(Mongo.java:772) ~[mongo-java-driver-3.2.2.jar!/:na]
    at com.mongodb.Mongo$2.execute(Mongo.java:759) ~[mongo-java-driver-3.2.2.jar!/:na]
    at com.mongodb.OperationIterable.iterator(OperationIterable.java:47) ~[mongo-java-driver-3.2.2.jar!/:na]
    at com.mongodb.FindIterableImpl.iterator(FindIterableImpl.java:143) ~[mongo-java-driver-3.2.2.jar!/:na]
    at myapp.common.db.service.dao.AnalysisMongoImpl.getAnalysis(AnalysisMongoImpl.java:66) ~[common-0.2.0-SNAPSHOT.jar!/:na]
    at myapp.common.db.service.AnalysisServiceImpl.getAnalysis(AnalysisServiceImpl.java:31) ~[common-0.2.0-SNAPSHOT.jar!/:na]
    at myapp.aggregator.service.Api$1.handle(Api.java:88) ~[aggregator-0.2.0-SNAPSHOT.jar!/:na]
    at spark.webserver.MatcherFilter.doFilter(MatcherFilter.java:139) [spark-core-1.1.1.jar!/:na]
    at spark.webserver.JettyHandler.doHandle(JettyHandler.java:54) [spark-core-1.1.1.jar!/:na]
    at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:179) [jetty-server-9.0.2.v20130417.jar!/:9.0.2.v20130417]
    at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:136) [jetty-server-9.0.2.v20130417.jar!/:9.0.2.v20130417]
    at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:97) [jetty-server-9.0.2.v20130417.jar!/:9.0.2.v20130417]
    at org.eclipse.jetty.server.Server.handle(Server.java:451) [jetty-server-9.0.2.v20130417.jar!/:9.0.2.v20130417]
    at org.eclipse.jetty.server.HttpChannel.run(HttpChannel.java:252) [jetty-server-9.0.2.v20130417.jar!/:9.0.2.v20130417]
    at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:266) [jetty-server-9.0.2.v20130417.jar!/:9.0.2.v20130417]
    at org.eclipse.jetty.io.AbstractConnection$ReadCallback.run(AbstractConnection.java:240) [jetty-io-9.0.2.v20130417.jar!/:9.0.2.v20130417]
    at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:596) [jetty-util-9.0.2.v20130417.jar!/:9.0.2.v20130417]
    at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:527) [jetty-util-9.0.2.v20130417.jar!/:9.0.2.v20130417]
    at java.lang.Thread.run(Thread.java:745) [na:1.7.0_95]
Caused by: java.net.SocketTimeoutException: Read timed out
    at java.net.SocketInputStream.socketRead0(Native Method) ~[na:1.7.0_95]
    at java.net.SocketInputStream.read(SocketInputStream.java:152) ~[na:1.7.0_95]
    at java.net.SocketInputStream.read(SocketInputStream.java:122) ~[na:1.7.0_95]
    at com.mongodb.connection.SocketStream.read(SocketStream.java:85) ~[mongo-java-driver-3.2.2.jar!/:na]
    at com.mongodb.connection.InternalStreamConnection.receiveResponseBuffers(InternalStreamConnection.java:491) ~[mongo-java-driver-3.2.2.jar!/:na]
    at com.mongodb.connection.InternalStreamConnection.receiveMessage(InternalStreamConnection.java:221) ~[mongo-java-driver-3.2.2.jar!/:na]
    ... 34 common frames omitted

person Enrichman    schedule 24.02.2016    source источник
comment
Вам нужно опубликовать исключение, его сообщение и трассировку стека. В вашем вопросе. Ни одно из этих полей-членов или методов не должно быть статическим.   -  person user207421    schedule 24.02.2016
comment
@EJP хм, почему? Я предполагаю, что проблема может быть в getDB(), но, как я читал, я должен обрабатывать MongoClient как синглтон.   -  person Enrichman    schedule 24.02.2016
comment
Клиент != Курсор && Клиент != Соединение. На самом деле клиент поддерживает пул соединений. Используя эти соединения, на сервере выполняются операции, такие как запросы. В этом случае возвращается курсор. Если операция занимает слишком много времени, инициируется тайм-аут, и соединение закрывается на стороне сервера. Что, насколько я вижу, и произошло.   -  person Markus W Mahlberg    schedule 24.02.2016
comment
@MarkusWMahlberg Я не думаю, что это так. У меня таймаут запроса, и эти кидаются еще и для не тяжелых агрегаций. В случае длительных запросов выдается правильное исключение. collection.aggregate(pipeline).maxTime(maxTimeout, TimeUnit.SECONDS); и из логов видно, что исключение возбуждается ДО агрегации.   -  person Enrichman    schedule 24.02.2016
comment
Что ж, я бы перепроверил предположение, что агрегаты должны быть достаточно быстрыми, запустив их в оболочке. Плохой индекс в сочетании с высокой нагрузкой может нанести ущерб производительности, в то время как без нагрузки все было в порядке.   -  person Markus W Mahlberg    schedule 24.02.2016
comment
@MarkusWMahlberg Я отредактировал предыдущий комментарий. Просто указываю, что запрос агрегации даже не выполняется при повышении SocketTimeout. Кроме того, агрегация выполняется непосредственно в поле _id без какой-либо нагрузки на сервер.   -  person Enrichman    schedule 24.02.2016
comment
Ну, это не то, что я вижу из трассировки стека, однако вы, безусловно, правы. Удачи! ;)   -  person Markus W Mahlberg    schedule 24.02.2016
comment
@MarkusWMahlberg хм, я понимаю, что ты пытаешься сказать. На самом деле да, он возникает перед агрегацией, но во время другого запроса. Но это простая находка, сделанная _id, так что я все еще в замешательстве. :( (извините за тон, немного устал разбираться в этом)   -  person Enrichman    schedule 24.02.2016
comment
Давайте продолжим обсуждение в чате.   -  person Markus W Mahlberg    schedule 24.02.2016


Ответы (3)


После нескольких попыток я обнаружил, что проблема связана с Балансировщик нагрузки Azure.
После 60-секундного бездействия он отключит все ожидающие TCP-соединения.

После дальнейших копаний я нашел этот пост Часто задаваемые вопросы по диагностике MongoDB, и я установил tcp keepalive на 120 с:

sudo sysctl -w net.ipv4.tcp_keepalive_time=<value>

и я также установил socketKeepAlive для MongoClient к истине:

MongoClientOptions.Builder options = MongoClientOptions.builder();
options.socketKeepAlive(true);
mongoClient = new MongoClient(mongoAddress, options.build());

После этих исправлений проблема исчезла!

person Enrichman    schedule 03.03.2016
comment
Где вы запускали эту команду? sudo sysctl -w net.ipv4.tcp_keepalive_time=‹значение› - person VaidAbhishek; 07.07.2016
comment
Непосредственно на сервере (linux) - person Enrichman; 07.07.2016
comment
socketKeepAlive устарел - person Riccardo; 27.02.2020
comment
устарели в пользу чего? твой комментарий наполовину полезен - person Pratik Patel; 19.03.2020
comment
Он устарел в пользу того, чтобы просто не устанавливать его. Из JavaDoc; настройка поддержки активности устарела. Теперь по умолчанию установлено значение true, и отключать его не рекомендуется. - person TomPlum; 29.07.2020

Если возникают ошибки сокетов между клиентами и серверами или между элементами сегментированного кластера или набора реплик, которые не имеют других разумных причин, проверьте значение проверки активности TCP (например, значение tcp_keepalive_time в системах Linux). Обычный период поддержки активности составляет 7200 секунд (2 часа); однако разные дистрибутивы и macOS могут иметь разные настройки.

Для MongoDB у вас будут лучшие результаты с более коротким периодом проверки активности, порядка 120 секунд (двух минут).

Там, где вы установили mongodb, вам нужно просто запустить эту команду в Linux.

sudo sysctl -w net.ipv4.tcp_keepalive_time=120

Ссылка: Влияет ли время проверки активности TCP Развертывания MongoDB?

person Prashant Agarwal    schedule 09.03.2018
comment
У меня уже есть net.ipv4.tcp_keepalive_time = 120 на хосте, и я считаю, что сейчас для параметра keep-alive по умолчанию установлено значение true. Однако я все еще иногда получаю org.mongodb.driver.connection : Got socket exception on connection. - person daniyel; 05.02.2019
comment
@NitishDeshpande Ну, я сейчас уже не помню, так как мы пробовали много разных вещей, и я также больше не работаю в компании в то время. Если я не ошибаюсь, мы сделали что-то вроде логики повторной попытки, когда Azure разрывал соединение, мы его восстанавливали. У меня нет фрагмента, чтобы продемонстрировать, как мы это решили. - person daniyel; 12.03.2020
comment
@NitishDeshpande Вам удалось решить эту проблему? - person Amol Kshirsagar; 17.04.2020

Я только что столкнулся с той же проблемой и решил ее, увеличив spring.data.mongodb.socketTimeout=<value> в конфигурации Spring Boot. По умолчанию значение равно 0 и означает отсутствие тайм-аута. Однако мой коллега установил его на 20000 (20 секунд), поэтому MongoSocketReadTimeoutException будет выброшен, как только мой aggregate() будет работать дольше 20 секунд!

person Zhang Chen    schedule 19.01.2021