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

Posted

技术标签:

【中文标题】配置 GlassFish JDBC 连接池以处理 Amazon RDS 多可用区故障转移【英文标题】:Configure GlassFish JDBC connection pool to handle Amazon RDS Multi-AZ failover 【发布时间】:2014-09-17 11:28:39 【问题描述】:

我有一个 Java EE 应用程序在 EC2 上的 GlassFish 中运行,并且在 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 分钟。

如果我使用 多可用区 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 检测数据库故障转移?

【问题讨论】:

你能在哪里解决这个问题? @hectorg87 不是真的。我发现如果我设置更高数量的 HTTP 线程,服务器将创建与数据库的新连接,并在数据库可用时立即恢复。但是,旧连接仍会阻塞 15 分钟,如果数据库连接池耗尽,此设置将中断。 该死!我假设它与池本身无关,而与 Java 的 DNS 缓存有关。它正在缓存旧数据库的 IP 地址,并且在故障转移完成后永远不会更改为新数据库。我会在禁用 Java 的 DNS 缓存后立即通知您。也许我什至会回答你的问题:-) 你好@Andrea,我回来了。我通过在 jdbc 连接字符串上设置 socketTimeout 来解决它,例如 jdbc:(...)&connectTimeout=15000&socketTimeout=60000&autoReconnect=true 我还通过执行 java.security.Security.setProperty("networkaddress.cache.ttl) 禁用了 Java 的 DNS 缓存" , "0"); java.security.Security.setProperty("networkaddress.cache.negative.ttl" , "0");虽然由于您使用的是应用程序服务器,但在使用 -Dnet 启动 glassfish 时必须将禁用 DNS 缓存的参数传递给 JVM.... :-) @hectorg87 是的!我已将我的 create-jdbc-connection-pool 命令更改为使用明确的 jdbc URL,就像您建议的那样:jdbc:mysql://$DBHOST:$DBPORT/$DBNAME?connectTimeout=15000&socketTimeout=60000&autoReconnect=true,它可以工作!我不需要更改 DNS 的东西(RDS 提供的域名的 TTL 为 5 秒)。请提供一个我可以投票的答案。谢谢! 【参考方案1】:

正如我之前评论的,这是因为打开并连接到数据库的套接字没有意识到连接已经丢失,所以它们一直保持连接直到触发 OS 套接字超时,我读到的可能通常在大约 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。

【讨论】:

以上是关于配置 GlassFish JDBC 连接池以处理 Amazon RDS 多可用区故障转移的主要内容,如果未能解决你的问题,请参考以下文章

Glassfish“连接已关闭”错误与连接池、JDBC 和 SQL Server 2008

Glassfish中的JDBC连接池ping错误

glassfish JDBC 连接池

Glassfish 应用服务器中的 JDBC 和连接池

在 GlassFish v4.1.1 中创建 jdbc 连接池 [重复]

JDBC连接池设置无法在Glassfish5上运行