#透明应用程序故障转移
TAF是一项客户端功能,能最大程度地减少数据库连接因实例或网络故障而失败时对最终用户应用程序的中断。
# URL配置参数
YashanDB JDBC中存在如下TAF功能相关的URL参数:
| 参数名称 | 数据类型 | 参数说明 |
|---|---|---|
| failover | string | 是否开启TAF,参数值为ON或OFF,默认为OFF。 * ON:表示开启TAF,当故障发生时应用程序会自动切换到已配置的其他数据库节点。 * OFF:表示关闭TAF,且暂不支持在OFF状态下自动重连当前数据库节点。 |
| failoverType | string | 故障转移的类型,支持NONE、SESSION和SELECT,默认为NONE。 * NONE:表示不使用故障转移。 * SESSION:表示发生故障后重连并重置Statement句柄。 * SELECT:表示在SESSION类型的基础上,切换连接后重新执行因故障而执行出错的SELECT语句(此时,SELECT语句不能为带LOB类型的绑定参数的查询语句),但不支持再对切换前已获取的对象进行二次操作。 |
| failoverMethod | string | 故障转移的方式,该值将决定从主节点到备节点的故障转移速度。 * BASIC:表示在故障转移时再建立连接。 |
| failoverRetries | int | 重试次数,默认值为5。 |
| failoverDelay | int | 重试间隔时间(单位:秒),默认值为1。 |
URL参数格式为:
jdbc:yasdb:serverType://host1:port1,host2:port2;host3:port3,host4:port4/yashan?key1=value1&key2=value2
URL连接配置示例:
jdbc:yasdb:primary://192.168.1.2:1688,192.168.1.3:1688;192.168.1.4:1688,192.168.1.5:1688;192.168.1.6:1688,192.168.1.7:1688/yashan?poolTimeout=180&failover=on&failoverType=session&failoverMethod=basic&failoverRetries=5&failoverDelay=1
配置多组IP/PORT连接时,组内按照serverType类型连接,组间按照顺序优先级访问,例如有3组IP/PORT连接,当前发生TAF的节点在第2组,则先访问第2组其他节点,再访问第1组节点,最后访问第3组节点。
# TAF相关接口
TAF功能相关的接口如下:
| 类 | 返回类型 | 方法 | 参数 | 说明 |
|---|---|---|---|---|
| YasConnection | void | registerTAFCallback(YasFailover yasFailover, Object ctxt) | [1] yasFailover:用户注册实现callbackFn方法的实体类 [2] ctxt:用户想要保存的任何对象 | 注册TAF回调函数的实现类,如果注册了回调函数,failoverRetries和failoverDelay参数不生效,TAF链接失败时不会尝试进行重连。 |
| YasFailover | void | callbackFn (Connection conn, Object ctxt, int type, int event) | [1] conn:当前的链接 [2] ctxt:用户想要保存的任何对象 [3] type:故障转移类型 [4] event:event | TAF的回调函数,该方法由应用程序继承实现。目前只支持FO_BEGIN、FO_ERROR、FO_RETRY、FO_END,在FO_ERROR事件中如果返回FO_RETRY则表示再尝试进行一次连接。 |
# 使用示例
当数据库服务端发生故障时,配置了TAF功能的JDBC驱动能够自动重连或切换到活跃实例。切换成功后可能会抛出相关异常,开发人员可以捕获对应的异常信息并根据信息分析判断是否需要执行相应措施(例如回滚事务等)。
异常信息为"The transaction must be rerun.",表示当前行为内触发了TAF且已成功重连,但当前操作处于事务过程中,服务端的事务已经回滚,此时应用层需要执行connection.rollback()并且重新执行事务。
异常信息为"Cannot be safely called repeatedly.",表示当前行为内触发了TAF且已成功重连,此时需要应用层在确保安全的情况下重新执行SQL。
异常信息为"Failover reconnect fail",表示当前行为内触发了TAF,但重连失败。此时应检查网络是否正常、检查数据库是否正常运行。
在事务过程中发生TAF的示例如下:
public static void main(String[] args) throws Exception {
String className = "com.yashandb.jdbc.Driver";
String url = "jdbc:yasdb://192.168.1.2:1688,192.168.1.3:1688/test?failover=on&failoverType=session&failoverMethod=basic&failoverRetries=100&failoverDelay=15";
String user = "user"; //用户名
String password = "password";//密码
Class.forName(className);
Connection connection = DriverManager.getConnection(url, user, password);// 获取连接
connection.setAutoCommit(false);//开启事务
dealTafInTransaction(connection);
}
private static void dealTafInTransaction(Connection connection) throws SQLException {
try (Statement statement = connection.createStatement()) {
//sql1、sql2、sql3表示事务中需要执行的SQL语句,此处不列示具体内容
String sql1 = "...";
String sql2 = "...";
String sql3 = "...";
try {
statement.execute(sql1);
statement.execute(sql2);
statement.execute(sql3);
connection.commit();//提交事务
} catch (SQLException e) {
connection.rollback();//必须执行rollback,否则后续事务内任何语句或commit操作都将报错
if ("The transaction must be rerun.".equals(e.getMessage())) {// 出现该异常信息说明taf已经重连成功,此时可以选择重新执行整个事务
dealTafInTransaction(connection);
}
throw e;
}
}
}
不在事务过程中发生TAF的示例如下:
private static final String TAF_ERROR_MSG = "Cannot be safely called repeatedly.";
public static void main(String[] args) throws Exception {
String className = "com.yashandb.jdbc.Driver";
String url = "jdbc:yasdb://192.168.1.2:1688,192.168.1.3:1688/test?failover=on&failoverType=session&failoverMethod=basic&failoverRetries=100&failoverDelay=15";
String user = "user"; //用户名
String password = "password";//密码
Class.forName(className);
Connection connection = DriverManager.getConnection(url, user, password);// 获取连接
connection.setAutoCommit(false);//不开启事务
dealTafWithoutTransaction(connection);
}
public static void dealTafWithoutTransaction(Connection connection) throws SQLException {
try (Statement statement = connection.createStatement()) {
//sql1、sql2表示需要执行的SQL语句,此处不列示具体内容
String sql1 = "...";
String sql2 = "...";
try {
statement.execute(sql1);
} catch (SQLException e) {
if (TAF_ERROR_MSG.equals(e.getMessage())) { // 出现该异常信息说明taf已经重连成功,此时可以选择重新执行该语句
statement.execute(sql1);
}
throw e;
}
//对于带参数的SQL语句(假设sql2是一条带参数绑定的SQL语句),若执行过程中发生TAF,无需重复绑定参数
PreparedStatement preparedStatement = connection.prepareStatement(sql2);
preparedStatement.setInt(1,1);
preparedStatement.setString(2,"2");//参数绑定
try {
preparedStatement.execute();
} catch (SQLException e) {
if (TAF_ERROR_MSG.equals(e.getMessage())) {//sql语句执行中发生了taf,只需要重新执行该语句即可,不需要重新进行参数绑定了
preparedStatement.execute();
}
throw e;
}
}
}

