MariaDB JDBC 驱动程序是否忽略了连接超时?



在我们正在进行的应用程序中,用户可以通过在文本字段中输入任意JDBC连接URL来连接到外部RDBMS。我们的一位客户报告说,当他意外尝试使用 MySQL JDBC URL 连接到Microsoft SQL Server 时,我们的应用程序服务器(无限期(冻结在 0% CPU 上。

以下 Java 代码段说明了这种情况:

public static void main(String[] args){
// note: the application running on localhost:1433 is ACTUALLY
// an MS SQL Server instance!
String jdbcUrl = "jdbc:mysql://localhost:1433/my-db";
// enable JDBC Driver Manager logging
DriverManager.setLogWriter(new PrintWriter(System.err));
// set a timeout of 5 seconds for connecting (which is blissfully ignored!)
DriverManager.setLoginTimeout(5);
// open the connection (which should fail, but freezes instead)
try (Connection c =  DriverManager.getConnection(jdbcUrl)){
System.out.println("This should never be reached due to wrong JDBC URL.");
}catch(Exception e){
System.out.println("This is expected (but never printed).");
}
System.out.println("This is never printed either.");
}

要运行代码段,请执行以下操作:

  • 在本地主机上运行 SQL Server 实例:1433(内容无关紧要(
  • 具有 MariaDB JDBC 驱动程序版本 2.2.5。(最新(在您的类路径上。

问题:

  • 1(这可能是MariaDB JDBC驱动程序中的错误吗?谷歌搜索在这方面没有任何发现。
  • 2( 我应该如何解决此问题?我不希望我的服务器在用户意外插入无效的 JDBC URL 时冻结。

我尝试了其他几个JDBC驱动程序(MySQL,DB2,Oracle...(,它们都优雅地处理了这个问题,只有MariaDB JDBC驱动程序冻结了JVM。

这是我为解决问题所做的。诀窍是向连接添加socketTimeout。要修复问题中的程序,只需将 JDBC URL 修改为:

jdbc:mysql://localhost:1433/my-db?socketTimeout=2000

这个相关问题的答案是我需要的提示。

答案 1:是的,这是一个错误。他们错过了在mariadb jdbc驱动程序的实现中使用登录超时。

答案 2:我通过使用包装 getConnection 方法的任务来解决。如果此任务尚未完成,则会在定义的登录时间后停止。这是我的实现。

import java.sql.Connection;
import java.sql.DriverManager;
import java.util.Properties;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import org.mariadb.jdbc.util.DefaultOptions;
public class ConnectionTest {
private static final String CONNECTION_STRING = "jdbc:mariadb://localhost:3306/test";
private static final String USER = "root";
private static final String PW = "";
private static final int LOGIN_TIMEOUT_SEC = 2;
public static void main(String[] args) throws Exception {
var test = new ConnectionTest();
Connection connection = test.getConnection();
if(connection != null && connection.isValid(LOGIN_TIMEOUT_SEC)) {
System.out.println("Connected!");
}
}
private Connection getConnection() throws Exception {
ConnEstablishSync sync = new ConnEstablishSync();
Properties conProps = new Properties();
conProps.setProperty(DefaultOptions.USER.getOptionName(), USER);
conProps.setProperty(DefaultOptions.PASSWORD.getOptionName(), PW);
FutureTask<Connection> task = new FutureTask<>(() -> {
Connection c = DriverManager.getConnection(CONNECTION_STRING, conProps);
if(sync.canceled && c != null) {
c.close();
c = null;
}
return c;
});
Connection connection = null;
ExecutorService executor = Executors.newSingleThreadExecutor();
try {
executor.submit(task);
connection = task.get(LOGIN_TIMEOUT_SEC, TimeUnit.SECONDS);
} finally {
sync.canceled = true;
task.cancel(true);
executor.shutdown();
}
return connection;
}
private static class ConnEstablishSync {
private volatile boolean canceled = false;
}
}

最新更新