Спящий режим теряет соединение

Я использую hibernate для подключения моей базы данных mysql и выполнения транзакций.

Я использую один SessionFactory во всем приложении, и у меня нет других подключений к базе данных, но я получаю исключение:

java.io.EOFException: Can not read response from server. Expected to read 4 bytes, read 0 bytes before connection was unexpectedly lost.
    at com.mysql.jdbc.MysqlIO.readFully(MysqlIO.java:3008)
    at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:3466)
    ... 21 common frames omitted
Wrapped by: com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure

The last packet successfully received from the server was 526 milliseconds ago.  The last packet sent successfully to the server was 1 milliseconds ago.
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
    at com.mysql.jdbc.Util.handleNewInstance(Util.java:425)
    at com.mysql.jdbc.SQLError.createCommunicationsException(SQLError.java:989)
    at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:3556)
    at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:3456)
    at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3897)
    at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2524)
    at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2677)
    at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2545)
    at com.mysql.jdbc.ConnectionImpl.setAutoCommit(ConnectionImpl.java:4842)
    at org.hibernate.engine.jdbc.connections.internal.PooledConnections.poll(PooledConnections.java:84)
    at org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl.getConnection(DriverManagerConnectionProviderImpl.java:186)
    at org.hibernate.internal.NonContextualJdbcConnectionAccess.obtainConnection(NonContextualJdbcConnectionAccess.java:35)
    at org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl.acquireConnectionIfNeeded(LogicalConnectionManagedImpl.java:99)
    ... 11 common frames omitted
Wrapped by: org.hibernate.exception.JDBCConnectionException: Unable to acquire JDBC Connection
    at org.hibernate.exception.internal.SQLStateConversionDelegate.convert(SQLStateConversionDelegate.java:115)
    at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:42)
    at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:111)
    at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:97)
    at org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl.acquireConnectionIfNeeded(LogicalConnectionManagedImpl.java:102)
    at org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl.getPhysicalConnection(LogicalConnectionManagedImpl.java:129)
    at org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl.getConnectionForTransactionManagement(LogicalConnectionManagedImpl.java:247)
    at org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl.begin(LogicalConnectionManagedImpl.java:254)
    at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl.begin(JdbcResourceLocalTransactionCoordinatorImpl.java:203)
    at org.hibernate.engine.transaction.internal.TransactionImpl.begin(TransactionImpl.java:56)
    at org.hibernate.internal.AbstractSharedSessionContract.beginTransaction(AbstractSharedSessionContract.java:387)
    at com.kitaplist.common.book.dao.HibernateBookDao.find(HibernateBookDao.java:56)
    at com.kitaplist.common.Collector.lambda$collectMetaBooksAndNewBooks$1(Collector.java:137)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)

Вот как я создаю свой SessionFactory:

public static SessionFactory getSessionFactory() {

    if (sessionFactory == null) {
        sessionFactory = new Configuration()
                .configure()
                .addAnnotatedClass(Seller.class)
                .addAnnotatedClass(Book.class)
                .buildSessionFactory();
    }

    return sessionFactory;
}

и вот функция, которую я использую в своем BookDao:

@Override
public void save(Book book) {
    Session session = sessionFactory.openSession();
    Transaction tx = session.beginTransaction();
    try {
        session.save(book);
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        try {
            tx.commit();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            session.close();
        }

    }
}

мое приложение искатель сканирует объект книги из Интернета и сохраняет объект в базе данных с помощью вышеуказанной функции сохранения. Я не мог найти причину этого исключения.

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

SLF4J: A number (289) of logging calls during the initialization phase have been intercepted and are
SLF4J: now being replayed. These are subject to the filtering rules of the underlying logging system.
SLF4J: See also http://www.slf4j.org/codes.html#replay
Wed Mar 14 16:36:29 UTC 2018 WARN: Establishing SSL connection without server identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
Wed Mar 14 16:36:29 UTC 2018 WARN: Establishing SSL connection without server identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
Wed Mar 14 16:36:30 UTC 2018 WARN: Establishing SSL connection without server identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
Wed Mar 14 16:36:29 UTC 2018 WARN: Establishing SSL connection without server identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.


Wed Mar 14 16:47:14 UTC 2018 WARN: Establishing SSL connection without server identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.

Wed Mar 14 16:47:17 UTC 2018 WARN: Establishing SSL connection without server identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.

Буду признателен за любую помощь.

Ответ 1

Поскольку ваша проблема связана с сетью, лучшей идеей может быть добавление пула подключений JDBC к вашей настройке. Это гарантирует, что соединения не будут устаревшими во время работы вашего приложения, например, из-за таймаута на стороне сервера базы данных.

HikariCP - это, безусловно, мой любимый пул, поскольку он устанавливает разумные значения по умолчанию, например, проверку на основе заимствования соединения, которая не может быть отключена. Поскольку вы используете Hibernate, HikariCP показывает, как настроить все на своей вики-странице. Однако правильная настройка может зависеть от других библиотек, которые вы используете, например, совместно java.sql.DataSource компонента java.sql.DataSource в Spring Boot.

Существуют и другие библиотеки пула, которые вы можете использовать, но, пожалуйста, пройдите несколько минут, чтобы прочитать ниже, прежде чем идти по этому маршруту:

Если использование библиотеки пула с проверкой подключений по-заимствования не решит эту проблему, вам придется погрузиться в настройку сети. Возможно, между вашим приложением и базой данных есть брандмауэр, который завершает любое сетевое соединение, например, путем наложения жесткого тайм-аута. Обычно эта ситуация разрешима путем установки интервала проверки соединения в пуле, который ниже таймаута, налагаемого сетью.

Ответ 2

Основываясь на вашем описании проблемы, я считаю, что она отбрасывается, потому что она становится idd...

Попробуйте добавить '? AutoReconnect = true' в конец URL-адреса JDBC базы данных... и посмотреть, не возникла ли проблема больше...

Однако, если вы не можете подключиться к базе данных даже один раз, я предлагаю проверить следующие пункты:

  1. Сделайте команду ping для вашей базы данных IP от хоста сервера
  2. Сделайте команду telnet в своей базе данных, чтобы узнать, можете ли вы добраться до порта базы данных
  3. Посмотрите, не существует ли у MySql правил о том, какие IP-адреса могут говорить с ним (я знал, что postgres имеют эту функцию, не знаю, делает ли MySql)
  4. Убедитесь, что у MySql нет каких-то подключений по ссылке.
  5. Проверьте параметры подключения JDBC

Ответ 3

Главным виновником является wait_timeout. Его значение по умолчанию - 28800 с, т.е. 8 часов. Из документа:

Количество секунд, в течение которых сервер ожидает активности в неинтерактивном соединении, прежде чем закрывать его.

Ошибка, которую вы получаете, возникает, когда соединение с БД простаивает (не выполняет какой-либо запрос БД) для wait_timeout secs. После этого времени MySQL отключает соединение, а вы, когда ваш код выполняет любые вызовы БД, получает эту ошибку.

Увеличьте это значение (скажем, 1 день), и вы можете обойти эту проблему.


Однако, чтобы исправить эту проблему, поместите autoReconnect=true в строку подключения DB, как autoReconnect=true ниже:

jdbc:mysql://db_user:[email protected]/mydb?autoReconnect=true

Это приведет к тому, что код автоматически подключит соединение, когда соединение будет отключено после wait_timeout secs.

Ответ 4

Хорошо, когда мы видим, что журнал прикреплен, он говорит:

The last packet successfully received from the server was 526 milliseconds ago.  The last packet sent successfully to the server was 1 milliseconds ago.

Это не вопрос тайм-аута наверняка. Поскольку свободное время ожидания составляет около 500 мс.

java.io.EOFException: Can not read response from server. Expected to read 4 bytes, read 0 bytes before connection was unexpectedly lost.
    at com.mysql.jdbc.MysqlIO.readFully(MysqlIO.java:3008)


Wrapped by: org.hibernate.exception.JDBCConnectionException: Unable to acquire JDBC Connection
    at org.hibernate.exception.internal.SQLStateConversionDelegate.convert(SQLStateConversionDelegate.java:115)

Похоже, что соединение с базой данных блокируется/прерывается.

Вероятно, это также означает, что база данных перезапустилась или сетевое соединение с базой данных было нарушено (например, время подключения к NAT было отключено) или может быть проблемой межсетевого экрана.

AutoReconnect не рекомендуется. Из MySQL здесь

Должен ли водитель попытаться восстановить устаревшие и/или мертвые соединения? Если включено, драйвер будет генерировать исключение для запросов, выданных на устаревшем или мертвом соединении, которые относятся к текущей транзакции, но попытается повторно подключиться перед следующим запросом, выпущенным при соединении в новой транзакции. Использование этой функции не рекомендуется, поскольку она имеет побочные эффекты, связанные с состоянием сеанса и согласованностью данных, когда приложения не обрабатывают правильные SQLExceptions и предназначены только для использования, когда вы не можете настроить приложение для обработки SQLExceptions, возникающих в результате мертвые и устаревшие соединения. В качестве альтернативы, в качестве последней опции, исследуйте настройку переменной сервера MySQL "wait_timeout" на высокое значение, а не по умолчанию 8 часов.

Дополнительное предложение:

чтобы избавиться от этого предупреждения: установление SSL-соединения без проверки подлинности сервера

use useSSL=false в конфигурации Mysql

например:

jdbc:mysql://localhost:3306/Peoples?autoReconnect=true&useSSL=false

Ответ 5

Ты можешь использовать

sessionFactory.isClosed();

чтобы определить, все ли соединение открыто. Замените метод getSessionFactory() следующим образом.

public static SessionFactory getSessionFactory() {

    if (sessionFactory == null || sessionFactory.isClosed()) {
        sessionFactory = new Configuration()
                .configure()
                .addAnnotatedClass(Seller.class)
                .addAnnotatedClass(Book.class)
                .buildSessionFactory();
    }

    return sessionFactory;
}

Ответ 6

   at com.mysql.jdbc.SQLError.createCommunicationsException(SQLError.java:989)
    at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:3556)
    at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:3456)
    at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3897)

Из вашей stacktrace кажется, что hibernate не может получить соединения из базы данных, поскольку сервер db ушел

    // Check return value, if we get a java.io.EOFException, the server has gone away. We'll pass it on up the exception chain and let someone higher up
   // decide what to do (barf, reconnect, etc).

Проверьте источник здесь:
http://grepcode.com/file/repo1.maven.org/maven2/mysql/mysql-connector-java/5.1.36/com/mysql/jdbc/MysqlIO.java#3774

Возможное объяснение:
Проблема кажется на стороне сервера базы данных не в вашем коде. Возможно, вам придется настроить настройки сервера mysql, и проблема будет решена. Данные, которые вы отправляете, больше, чем пакет, который отправляется по сети в базу данных.

Причины и решения:
https://dev.mysql.com/doc/refman/5.7/en/gone-away.html
http://befused.com/mysql/server-has-gone-away

Другие решения:
в файле my.cnf mysql добавьте следующие настройки

[mysqld]
max_allowed_packet=256M