Mam aplikację Java EE uruchomioną na Glassfish i łączącą się z MSSQL Server 2008 poprzez jTDS. Z nieznanego powodu połączenie z bazą danych zostaje nieoczekiwanie zamknięte podczas żądań. Aplikacja jest ogromna, ale tutaj znajduje się podsumowanie tego, jak wystąpił błąd:Połączenie z bazą danych nieoczekiwanie zamknęło się przez Glassfish, jTDS i SQL Server 2008
Podczas konfiguracji Glassfish, z utworzeniem puli połączeń z asadmin create-jdbc-connection-pool
i asadmin create-jdbc-resource
. Klasa źródła danych to net.sourceforge.jtds.jdbcx.JtdsDataSource
.
Kiedy Glassfish idzie w górę, wywołuje naszą implementację ServletContextListener.contextInitialized(), gdzie pobieramy źródło danych z JNDI. Źródło danych jest przechowywane na zmiennej statycznej.
Przez chwilę wszystko idzie dobrze. Wszystkie żądania są obsługiwane, a żadne połączenie nie jest zamykane. Nasza aplikacja wykonuje przetwarzanie przy użyciu modułów EJB Timera i MDB (Message Driven Bean).
To jest przykład onMessage()
realizacja:
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;
}
}
końcu zaczynamy się następujący wyjątek (zdarza się około 100 razy w ciągu jednej godziny):
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)
Wyjątkiem dzieje się losowo JDBC połączenia. Czasami jest podczas iteracji ResultSet, innym razem podczas wykonywania kwerendy.
w bardzo rzadkich przypadkach (7 razy w ciągu godziny) otrzymujemy ten wyjątek:
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)
również w bardzo rzadkich przypadkach (5 razy w ciągu godziny) otrzymujemy ten wyjątek:
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
W rzadkich przypadkach możemy uzyskać ten straszny wyjątek (NPE wewnątrz 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)
...
nie możemy odnaleźć dlaczego tak się dzieje. Używane połączenia nigdy nie stają się bezczynne przez ponad sekundę podczas żądania. Nie wiemy, kto zrzuca połączenie. Może to być niestabilność sieci, ale w takim razie jTDS powinien generować tylko wyjątki związane z siecią, prawda?
Inną opcją jest polityka lub konfiguracja puli połączeń Glassfish (może Glassfish przedwcześnie zamyka fizyczne połączenia), ale jak możemy to śledzić?
Wreszcie, MS SQL Server 2008 może zdalnie zrzucać połączenia, ale jak możemy monitorować stronę serwera, aby dowiedzieć się, czy tak się dzieje?
nie występują również problemy podczas używania sterownika JDBC Microsoftu? – extraneon
@extraneon Nie testowaliśmy sterownika Microsoft JDBC. Istnieje cała warstwa trwałości oparta na jTDS, a przejście na MS zajmie kilka dni. – fernacolo
Czy Twój kod łapie/ignoruje SQLExceptions? Czy używasz podstawowego DataSource lub connectionpool? –