【发布时间】: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 秒)。请提供一个我可以投票的答案。谢谢!
标签: mysql jdbc glassfish amazon-rds failover