У меня есть приложение 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 может удаленно сбрасывать соединения, но как мы можем контролировать серверную сторону, чтобы узнать, происходит ли это?
finally
илиcatch
. Мы используем базовый источник данных, предоставляемый Glassfish, который предположительно реализует пул соединений. Мы не используем API-интерфейсы ConnectionPoolDataSource, такие какgetPooledConnection()
. - person fernacolo   schedule 22.11.2011onMessage()
находится внутри MDB. Он обращается к статической переменной, инициализированнойServletContextListener
. Я посмотрю, сможем ли мы выполнить поиск JNDI, чтобы получитьDataSource
при каждом вызовеonMessage()
. - person fernacolo   schedule 23.11.2011