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

标签 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

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

如果我使用 Multi-AZ 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.

似乎每个 HTTP 线程都在无效连接上被阻塞而没有得到异常,因此没有机会执行连接验证。

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

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

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

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

最佳答案

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

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

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

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

这是我的 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 发生变化。

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

关于mysql - 配置 GlassFish JDBC 连接池以处理 Amazon RDS 多可用区故障转移,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24958610/

相关文章:

Glassfish:链接安全策略时出错

mysql - MySql 中的间隔数据类型

mysql - 如何在Mysql存储过程中查找列表中的重复/重复值?

php - 每个特定的工作日,sql 和 php

java - 选择更新之前是一个好方法还是反之亦然?

java - JSF 动态导航根本不起作用

java - 配置本地主机 https glassfish

python - sqlite3.操作错误: table test has no column named

mysql - Spark 无法从 SBT 找到 JDBC 驱动程序

java - 绑定(bind)用户实体和 GlassFish 主体