配置GlassFish JDBC连接池以处理Amazon RDS多可用区故障转移

时间:2014-07-25 14:55:35

标签: mysql jdbc glassfish amazon-rds failover

我在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检测数据库故障转移?

1 个答案:

答案 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。