Соединение с базой данных неожиданно закрыто с помощью Glassfish, jTDS и SQL Server 2008

У меня есть приложение Java EE, работающее на Glassfish и подключающееся к MSSQL Server 2008 через jTDS. По какой-то неизвестной причине соединение с базой данных неожиданно закрывается во время запросов. Приложение огромно, но вот краткое описание того, как происходит ошибка:

Во время установки Glassfish создайте пул соединений с asadmin create-jdbc-connection-pool и asadmin create-jdbc-resource. Класс источника данных — net.sourceforge.jtds.jdbcx.JtdsDataSource.

Когда Glassfish запускается, он вызывает нашу реализацию ServletContextListener.contextInitialized(), где мы извлекаем источник данных из JNDI. Источник данных хранится в статической переменной.

Какое-то время все идет нормально. Все запросы обрабатываются, и ни одно соединение не закрывается. Наше приложение выполняет обработку с использованием EJB-компонентов Timer и MDB (Message Driven Bean).

Это пример реализации onMessage():

public void onMessage(Message message) {
  this.message = message;
  this.connection = dataSource.getConnection(userName, password);
  try {
    doQuery1();
    doTransaction1();
    doTransaction2();
    doQuery2();
    doQuery3();
  } finally {
    this.connection.close();
    this.connection = null;
  }
}

В конце концов, мы начинаем получать следующее исключение (происходит около 100 раз в течение часа):

java.sql.SQLException: Invalid state, the Connection object is closed.
  at net.sourceforge.jtds.jdbc.ConnectionJDBC2.checkOpen(ConnectionJDBC2.java)
  at net.sourceforge.jtds.jdbc.ConnectionJDBC2.prepareStatement(ConnectionJDBC2.java)
  at net.sourceforge.jtds.jdbc.ConnectionJDBC2.prepareStatement(ConnectionJDBC2.java)
  at com.sun.gjc.spi.base.ConnectionHolder.prepareStatement(ConnectionHolder.java:475)
  at com.acme.myejbs.MyMDB.doQuery2(MyMDB.java:123)
  at com.acme.myejbs.MyMDB.onMessage(MyMDB.java:614)
  at sun.reflect.GeneratedMethodAccessor115.invoke(Unknown Source)
  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java)
  at java.lang.reflect.Method.invoke(Method.java)
  at com.sun.enterprise.security.application.EJBSecurityManager.runMethod(EJBSecurityManager.java:1011)
  ...
  at $Proxy92.onMessage(Unknown Source)
  at com.sun.messaging.jms.ra.OnMessageRunner.run(OnMessageRunner.java)
  at com.sun.enterprise.connectors.work.OneWork.doWork(OneWork.java:77)
  at com.sun.corba.ee.impl.orbutil.threadpool.ThreadPoolImpl$WorkerThread.run(ThreadPoolImpl.java:555)

Исключение возникает при случайных вызовах JDBC. Иногда во время итерации ResultSet, иногда во время выполнения запроса.

В очень редких случаях (7 раз в течение часа) мы получаем это исключение:

java.sql.SQLException: Error in allocating a connection. Cause: This Managed Connection is not valid as the phyiscal connection is not usable
  at com.sun.gjc.spi.base.DataSource.getConnection(DataSource.java:136)
  at com.acme.myejbs.MyMDB.onMessage(MyMDB.java:614)
  at sun.reflect.GeneratedMethodAccessor115.invoke(Unknown Source)
  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java)
  at java.lang.reflect.Method.invoke(Method.java)
  at com.sun.enterprise.security.application.EJBSecurityManager.runMethod(EJBSecurityManager.java:1011)
  ...
  at $Proxy92.onMessage(Unknown Source)
  at com.sun.messaging.jms.ra.OnMessageRunner.run(OnMessageRunner.java)
  at com.sun.enterprise.connectors.work.OneWork.doWork(OneWork.java:77)
  at com.sun.corba.ee.impl.orbutil.threadpool.ThreadPoolImpl$WorkerThread.run(ThreadPoolImpl.java:555)

Также в очень редких случаях (5 раз в течение часа) мы получаем это исключение:

java.sql.SQLException: I/O Error: Connection reset by peer: socket write error
  at net.sourceforge.jtds.jdbc.TdsCore.executeSQL(TdsCore.java)
  at net.sourceforge.jtds.jdbc.JtdsStatement.executeSQLQuery(JtdsStatement.java)
  at net.sourceforge.jtds.jdbc.JtdsPreparedStatement.executeQuery(JtdsPreparedStatement.java)
  at com.acme.myejbs.MyMDB.doQuery2(MyMDB.java:126)
  at com.acme.myejbs.MyMDB.onMessage(MyMDB.java:614)
  ...
Caused by: java.net.SocketException: Connection reset by peer: socket write error
  at java.net.SocketOutputStream.socketWrite0(Native Method)
  at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java)
  at java.net.SocketOutputStream.write(SocketOutputStream.java)
  at java.io.DataOutputStream.write(DataOutputStream.java)
  at net.sourceforge.jtds.jdbc.SharedSocket.sendNetPacket(SharedSocket.java)
  at net.sourceforge.jtds.jdbc.RequestStream.putPacket(RequestStream.java)
  at net.sourceforge.jtds.jdbc.RequestStream.flush(RequestStream.java)
  ... 44 more

В редких случаях мы получаем это страшное исключение (NPE внутри jTDS):

java.lang.NullPointerException
  at net.sourceforge.jtds.jdbc.JtdsPreparedStatement.executeQuery(JtdsPreparedStatement.java)
  at com.acme.myejbs.MyMDB.doQuery2(MyMDB.java:126)
  at com.acme.myejbs.MyMDB.onMessage(MyMDB.java:614)
  ...

Мы не можем найти, почему это происходит. Используемые соединения никогда не становятся бездействующими более чем на секунду во время запроса. Мы не знаем, кто прерывает связь. Это может быть нестабильность сети, но тогда я думаю, что jTDS должен выдавать только исключения, связанные с сетью, верно?

Другим вариантом является некоторая политика или конфигурация пула соединений Glassfish (возможно, Glassfish преждевременно закрывает физические соединения), но как мы можем это отследить?

Наконец, MS SQL Server 2008 может удаленно сбрасывать соединения, но как мы можем контролировать серверную сторону, чтобы узнать, происходит ли это?


person fernacolo    schedule 18.11.2011    source источник
comment
Возникают ли проблемы также при использовании драйвера Microsoft JDBC?   -  person extraneon    schedule 18.11.2011
comment
@extraneon Мы не тестировали драйвер Microsoft JDBC. Существует целый слой сохраняемости, основанный на jTDS, и переход на MS займет несколько дней.   -  person fernacolo    schedule 18.11.2011
comment
Ваш код перехватывает/игнорирует SQLExceptions? Вы используете базовый источник данных или пул соединений?   -  person Mark Rotteveel    schedule 22.11.2011
comment
Я не думаю, что мы игнорируем исключения, потому что трассировки стека взяты из обычного потока выполнения, а не из блоков finally или catch. Мы используем базовый источник данных, предоставляемый Glassfish, который предположительно реализует пул соединений. Мы не используем API-интерфейсы ConnectionPoolDataSource, такие как getPooledConnection().   -  person fernacolo    schedule 22.11.2011
comment
onMessage находится внутри одного сервлета? Попробуйте объявить соединение локальным вместо атрибута класса.   -  person    schedule 23.11.2011
comment
onMessage() находится внутри MDB. Он обращается к статической переменной, инициализированной ServletContextListener. Я посмотрю, сможем ли мы выполнить поиск JNDI, чтобы получить DataSource при каждом вызове onMessage().   -  person fernacolo    schedule 23.11.2011


Ответы (2)


Попробуйте использовать SQL Server Profiler http://msdn.microsoft.com/en-us/library/ms187929.aspx Вы можете начать со стандартного шаблона, поскольку он содержит события: Аудит входа, Аудит выхода, ExistingConnection http://msdn.microsoft.com/en-us/library/ms190176.aspx Я думаю, что они наиболее важны для ты

person AlexK    schedule 25.11.2011

У меня было приложение, которое почти точно получало эти типы исключений. Все мои машины были совершенно новыми серверами, и все сетевые карты были настроены на автоматическое определение скорости сети. Все они были подключены к старому коммутатору со скоростью 100 МБ/сек. ПОЛУДУПЛЕКСНЫЙ.

Настройка всех машин на этом коммутаторе для явного использования параметра HALF duplex со скоростью 100 МБ/с, а не автоматического распознавания, сработала для меня после бесчисленных часов поиска решения. Вам нужно выяснить, какими должны быть ваши настройки подключения, или поэкспериментировать (будет очевидно, если вы выберете неправильный вариант, потому что вы не сможете подключиться к приставке через удаленный рабочий стол, поэтому убедитесь, что вы можете получить доступ к физическая машина).

Это довольно низко висит фрукт, чтобы проверить это. Я настроил командное окно с командой ping с одной из рабочих машин, проверяющих связь с сервером базы данных, и периодически мог видеть потерю пакетов. Как только я изменил настройки сетевой карты и все сделал правильно, проблема полностью исчезла. В Интернете есть несколько статей, посвященных этой проблеме. Его сложно отследить, потому что он: 1) периодический и 2) похоже, что что-то не так с объектами соединения и т. д.

person Chris Townsend    schedule 23.11.2011