我在EC2上的GlassFish中运行了一个Java EE应用程序,在Amazon RDS上有一个MySQL数据库。 我正在尝试配置JDBC连接池,以便在数据库故障转移时最大限度地减少停机时间。
我的当前配置在多可用区故障转移期间无法正常运行,因为备用数据库实例似乎在几分钟内可用(根据AWS控制台),而我的GlassFish实例仍然长时间停留(恢复工作前约15分钟。
连接池的配置如下:
asadmin create-jdbc-connection-pool --restype javax.sql.ConnectionPoolDataSource \
--datasourceclassname com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource \
--isconnectvalidatereq=true --validateatmostonceperiod=60 --validationmethod=auto-commit \
--property user=$DBUSER:password=$DBPASS:databaseName=$DBNAME:serverName=$DBHOST:port=$DBPORT \
MyPool
如果我从控制台使用单一阵列 db.m1.small实例和重新启动数据库,GlassFish将使断开的连接无效,抛出一些例外,然后数据库可用后立即重新连接。在此设置中,我得到的停机时间不到1分钟。
如果我从AWS控制台使用多可用区 db.m1.small实例和使用故障转移重新启动,我看不出任何例外。服务器完全停止,所有传入的请求都超时。 15分钟后我终于得到了这个:
Communication failure detected when attempting to perform read query outside of a transaction. Attempting to retry query. Error was: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.3.2.v20111125-r10461): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure
The last packet successfully received from the server was 940,715 milliseconds ago. The last packet sent successfully to the server was 935,598 milliseconds ago.
似乎每个HTTP线程在无效连接上被阻止而没有获得异常,因此没有机会执行连接验证。
多可用区的停机时间总是在15-16分钟之间,所以它看起来像某种超时,但我无法改变它。
我尝试过的事情没有成功:
MysqlDataSource
代替MysqlConnectionPoolDataSource
如何在卡住的查询中设置超时,以便重用,验证和替换池中的连接? 或者我如何让GlassFish检测数据库故障转移?
答案 0 :(得分:4)
正如我之前评论的那样,这是因为打开并连接到数据库的套接字没有意识到连接已经丢失,所以它们保持连接直到触发OS套接字超时,我读到的可能是通常在30分钟左右。
要解决此问题,您需要在JDBC连接字符串或JDNI连接配置/属性中覆盖套接字Timeout,以将 socketTimeout 参数定义为更短的时间。
请记住,任何比定义的值更长的连接都会被杀死,即使它被使用(我还没能确认这一点,就是我读到的)。
我在评论中提到的其他两个参数是 connectTimeout 和 autoReconnect 。
这是我的JDBC连接字符串:
jdbc:(...)&connectTimeout=15000&socketTimeout=60000&autoReconnect=true
我还通过
禁用了Java的DNS缓存 java.security.Security.setProperty("networkaddress.cache.ttl" , "0");
java.security.Security.setProperty("networkaddress.cache.negative.ttl" , "0");
我这样做是因为Java并不尊重TTL,当发生故障转移时,DNS是相同的,但IP会发生变化。
由于您使用的是Application Server,因此在使用-Dnet而不是应用程序本身启动glassfish时,必须将禁用DNS缓存的参数传递给JVM。