C3P0 + MySQL: ошибка чтения пакетов связи - PullRequest
4 голосов
/ 14 февраля 2011

Ошибка многих людей связана с сообщением:

[Warning] Aborted connection 38 to db: 'database_name' user: 
'root' host: 'localhost' (Got an error reading communication packets)

который находится в логах MySQL. В моем случае доступ к базе данных локально осуществляется через клиент Java, используя драйвер com.mysql.jdbc.Driver и хорошо известный пул C3P0. Мой MySQL сервер настроен на прием довольно большого количества соединений, а значение max_allowed_packet установлено на 64M. Вот выдержка из моего файла my.cnf (конфигурация MySQL):

[mysqld]
max_allowed_packet  = 64M
thread_concurrency      = 8
thread_cache_size       = 8
thread_stack        = 192K
query_cache_size = 0
query_cache_type = 0
max_connections = 1024
back_log = 50
innodb_thread_concurrency = 6
innodb_lock_wait_timeout = 120
log_warnings

и

[mysqldump]
quick
quote-names
max_allowed_packet  = 64M

Таблица User в моей базе данных имеет следующую простую структуру:

CREATE TABLE `User` (
  `uid` varchar(255) COLLATE utf8_bin NOT NULL,
  `name` varchar(255) COLLATE utf8_bin DEFAULT NULL,
  `mail` varchar(255) COLLATE utf8_bin DEFAULT NULL,
  `password` varchar(255) COLLATE utf8_bin DEFAULT NULL,
  `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `maxParallelTasks` tinyint(4) NOT NULL DEFAULT '10',
  `maxModels` int(11) NOT NULL DEFAULT '2000',
  `maxBibTeX` int(11) NOT NULL DEFAULT '2000',
  PRIMARY KEY (`uid`) USING BTREE,
  UNIQUE KEY `mail` (`mail`) USING BTREE,
  KEY `uid` (`uid`) USING BTREE,
  KEY `index_user_name` (`name`) USING BTREE,
  KEY `index_user_mail` (`mail`) USING BTREE,
  KEY `index_user_maxModels` (`maxModels`) USING BTREE,
  KEY `index_user_maxBibTeX` (`maxBibTeX`) USING BTREE,
  KEY `index_user_maxParallelTasks` (`maxParallelTasks`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin

когда я INSERT значение в этой таблице от использования клиента mysql (т.е. mysql -u root -p), я не получаю предупреждений в журналах. Однако при попытке использовать ту же сторону Java вышеупомянутое предупреждение появляется в журналах довольно часто! Итак, на стороне Java мои соединения берутся из пула соединений C3P0, который настроен следующим образом:

datasource = new ComboPooledDataSource();
datasource.setJdbcUrl(connectURI);
datasource.setMaxPoolSize(1000);
datasource.setMinPoolSize(20);
datasource.setInitialPoolSize(50);
datasource.setNumHelperThreads(6);
datasource.setTestConnectionOnCheckin(true);
datasource.setTestConnectionOnCheckout(true);

Сначала подготовлено заявление:

PreparedStatement ps = connection.prepareStatement(getSql());

затем параметризовать:

ps.setString(1, user.getUid());
ps.setString(2, user.getName());
ps.setString(3, user.getMail());
ps.setString(4, user.getHashedPass());

и затем выполнено:

int update = ps.executeUpdate();

Подготовленное заявление закрывается:

ps.close();

и соединение SQL закрыто:

if (connection != null) {
            try {
                if (!connection.isClosed()) {
                    connection.close();
                }
            } catch (SQLException ex) {
                throw new DbException(ex);
            }
}

Общая процедура кажется (мне) законной. Согласно руководству по MySQL на http://dev.mysql.com/doc/refman/5.0/en/communication-errors.html, это предупреждение может означать, что:

Клиентская программа не вызывала mysql_close () перед выходом.

или что:

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

Для целей моего приложения я использовал C3P0 для записи и чтения в этих базах данных, используя более 200 параллельных потоков, и у меня нет утечек памяти, пока я проверял, что данные фактически передаются и извлекаются из базы данных нормально , У кого-нибудь еще есть подобный опыт?

Наконец, я включил мою версию MySQL, а также другую информацию, которая может быть полезна для устранения неполадок:

mysql> show variables like "%version%";
+-------------------------+-------------------+
| Variable_name           | Value             |
+-------------------------+-------------------+
| protocol_version        | 10                | 
| version                 | 5.1.37-1ubuntu5.5 | 
| version_comment         | (Ubuntu)          | 
| version_compile_machine | x86_64            | 
| version_compile_os      | debian-linux-gnu  | 
+-------------------------+-------------------+

и

Java version: 1.6.0_22, (build 1.6.0_22-b04)

Ответы [ 2 ]

1 голос
/ 15 февраля 2011

Попытка глубже исследовать причину этого предупреждения ...

Сначала я заметил, что количество предупреждений, которые я получаю в моем журнале, равно или очень близко к минимальному размеру пула, как указанона datasource.setMinPoolSize(int);, тогда как первоначальный размер пула для тестирования каждый раз устанавливался равным минимальному размеру.

Кроме того, используя отладчик и выполняя операторы шаг за шагом, я успел заметить, что предупреждения регистрируются, как только завершаются модульные тесты.Эти факты привели меня к выводу, что соединения, которые объединяются в C3P0, на самом деле не закрываются перед сбросом.Необходимо, чтобы источник данных был закрыт, когда он больше не будет использоваться, вызывая метод com.​mchange.​v2.​c3p0.​impl.​AbstractPoolBackedDataSource#close().Конечно, имеет смысл делать это только в конце модульного теста, поэтому в моем случае поправка выглядит следующим образом:

@AfterClass
public static void tearDownClass() throws Exception {
  // Close the pool 
  DataSourceFactory.getInstance().close();
}

В моем приложении я добавил хук отключения, чтобы источник данных закрылсядо того, как выполнение останавливается.До этого соединения в пуле оставались нетронутыми, и драйвер должен был их закрывать, что вызывало предупреждение в журнале MySQL.Это решило проблему для всех операций INSERT, но, к сожалению, не для операций SELECT.Обратите внимание, что я закрываю все наборы результатов, операторы, подготовленные операторы и соединения и проверил мой исходный код, используя FindBugs (инструмент статического анализа).Обходной путь заставляет меня подозревать, что с C3P0 снова что-то не так, но я не уверен.Таким образом, следующее делает предупреждения исчезающими ...

@AfterClass
public static void tearDownClass() throws Exception {
  Thread.sleep(100000); // wait for 100 secs
  DataSourceFactory.getInstance().close();
}

Обратите внимание, что до тех пор, пока не закончится период 100 сек (и фабрика источника данных не закроется), в журналах не появятся предупреждения!Итак, это datasource.close(), что вызывает их ...

1 голос
/ 14 февраля 2011

Хорошо, приятно видеть вопрос, который содержит почти правильное количество информации, которая намекает на возможные места, где система будет сталкиваться с проблемами.Вот что я бы проверял:

  • Попытайтесь закрыть объект PreparedStatement перед закрытием объекта Connection и убедитесь, что это решило проблему.Это может показаться ненужным, но требуется в случае определенных драйверов JDBC, в первую очередь Oracle (а также MySQL).Обоснование состоит в том, что объект Connection на самом деле не закрыт, особенно если производные объекты, такие как объекты ResultSet и Statement, не были закрыты первыми (в указанном порядке).Частично это связано с тем, как был написан драйвер JDBC.
  • Убедитесь, что задействованные машины действительно могут справиться с нагрузкой, которую вы хотите.Если базовая сетевая инфраструктура просто не может обработать указанное количество соединений, вполне возможно, что соединения будут сброшены.Возможно, вы захотите посмотреть на дамп проволочной акулы и сделать из него выводы, если первая рекомендация не поможет.
  • Используйте флаг debugUnreturnedConnectionStackTraces, чтобы обнаружить любые утечки из пула соединений.Это также требует, чтобы unreturnedConnectionTimeout был положительным.Этот флаг обеспечивает трассировку стека для условий, когда приложение не возвращает соединение обратно в пул.Трассировка стека укажет точку в коде, где соединение было изначально зарезервировано.
...