#透明应用程序故障转移

TAF是一项客户端功能,能最大程度地减少数据库连接因实例或网络故障而失败时对最终用户应用程序的中断。

# URL配置参数

YashanDB JDBC中存在如下TAF功能相关的URL参数:

参数名称 数据类型 参数说明
failover string 是否开启TAF,参数值为ON或OFF,默认为OFF。
* ON:表示开启TAF,当故障发生时应用程序会自动切换到已配置的其他数据库节点。
* OFF:表示关闭TAF,且暂不支持在OFF状态下自动重连当前数据库节点。
failoverType string TAF的类型,NONE,SESSION,SELECT三种类型,目前不支持SELECT,默认NONE,NONE表示关闭TAF。
failoverMethod string 故障转移的方式,该值将决定从主节点到备节点的故障转移速度。
* BASIC:表示在故障转移时再建立连接。
failoverRetries int 重试次数,默认值为5。
failoverDelay int 重试间隔时间(单位:秒),默认值为1。

单机部署(单库):

jdbc:yasdb://192.168.1.2:1688/yashan?failover=on&failoverType=session&failoverMethod=basic&failoverRetries=5&failoverDelay=1

单机高可用部署:

jdbc:yasdb:primary://192.168.1.2:1688,192.168.1.3:1688,192.168.1.4:1688/yashan?poolTimeout=180&failover=on&failoverType=session&failoverMethod=basic&failoverRetries=5&failoverDelay=1

负载均衡:

jdbc:yasdb:loadBalance://192.168.1.2:1688,192.168.1.3:1688,192.168.1.4:1688/yashan?failover=on&failoverType=session&failoverMethod=basic&failoverRetries=5&failoverDelay=1

# TAF相关接口

TAF功能相关的接口如下:

返回类型 方法 参数 说明
YasConnection void registerTAFCallback(YasFailover yasFailover, Object ctxt) [1] yasFailover:用户注册实现callbackFn方法的实体类
[2] ctxt:用户想要保存的任何对象
注册TAF回调函数的实现类,如果注册了回调函数,failoverRetries和failoverDelay参数不生效,TAF链接失败时不会尝试进行重连。
YasFailover int 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.”。出现该异常时,必须对事务显式执行回滚,否则事务内继续执行任何语句或commit操作都将报错,回滚后可以按需重新执行整个事务。
  • 不在事务过程中发生故障并切换,抛出的异常信息为“Cannot be safely called repeatedly.”。出现该异常时,可以按需选择直接重新执行或放弃执行目标语句,均不会影响到后续操作。

在事务过程中发生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;
            }
        }
    }

pdf-btn 下载文档 copy-btn 复制链接
edit-icon
反馈
coperate-icon
coperate
合作
communication-icon
communicate
交流