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

And*_*rea 5 mysql failover jdbc glassfish amazon-rds

我有一个在 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
Run Code Online (Sandbox Code Playgroud)

如果我使用可用区 db.m1.small 实例并从控制台重新启动数据库,GlassFish 将使断开的连接无效,抛出一些异常,然后在数据库可用时立即重新连接。在此设置中,我的停机时间不到 1 分钟。

如果我使用可用区 db.m1.small 实例并从 AWS 控制台通过故障转移重新启动,我看不到任何异常。服务器完全停止,所有传入请求都超时。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.
Run Code Online (Sandbox Code Playgroud)

看起来好像每个 HTTP 线程都在无效连接上被阻塞而没有出现异常,因此没有机会执行连接验证。

多可用区情况下的停机时间始终在 15-16 分钟之间,因此看起来像是某种超时,但我无法更改它。

我尝试过但没有成功的事情:

  • 连接泄漏超时/回收
  • 语句泄漏超时/回收
  • 语句超时
  • 使用不同的验证方法
  • 使用MysqlDataSource代替MysqlConnectionPoolDataSource

如何为卡住的查询设置超时,以便重用、验证和替换池中的连接?或者如何让 GlassFish 检测数据库故障转移?

hec*_*g87 5

正如我之前评论的那样,这是因为打开并连接到数据库的套接字没有意识到连接已丢失,因此它们保持连接,直到触发操作系统套接字超时,我读到的超时时间通常可能在 30 分钟左右。

要解决此问题,您需要覆盖 JDBC 连接字符串或 JDNI 连接配置/属性中的套接字超时,以将 socketTimeout参数定义为更小的时间。

请记住,任何比定义的值长的连接都将被终止,即使它正在使用(我无法确认这一点,这是我读到的)。

我在评论中提到的另外两个参数是connectTimeoutautoReconnect

这是我的 JDBC 连接字符串:

jdbc:(...)&connectTimeout=15000&socketTimeout=60000&autoReconnect=true 
Run Code Online (Sandbox Code Playgroud)

我还通过执行以下操作禁用了 Java 的 DNS 缓存

 java.security.Security.setProperty("networkaddress.cache.ttl" , "0"); 
 java.security.Security.setProperty("networkaddress.cache.negative.ttl" , "0"); 
Run Code Online (Sandbox Code Playgroud)

我这样做是因为 Java 不遵守 TTL,并且当发生故障转移时,DNS 是相同的,但 IP 会发生变化。

由于您使用的是应用程序服务器,因此在使用 -Dnet 启动 glassfish 时必须将禁用 DNS 缓存的参数传递到 JVM,而不是应用程序本身。