#透明故障转移

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

# TAF配置

# 配置参数

参数名以及参数值均不区分大小写,各参数信息如下表所示。

参数名 合法值 说明
FAILOVER ON:开启透明故障转移
OFF:关闭透明故障转移,默认为该值
该参数表示TAF是否开启。当故障发生时,TAF会链接到已配置的其他数据库节点。
FAILOVER_TYPE NONE:不使用透明故障转移,默认为该值
SESSION:当用户的连接丢失时,自动为其创建一个新会话
该参数指定故障转移的类型。
FAILOVER_METHOD BASIC:在故障转移时建立连接,默认为该值 该参数决定从主节点到备份节点的故障转移速度。
FAILOVER_RETRIES 默认值:5次 该参数指定故障转移后尝试连接的次数。
若配置了FAILOVER_DELAY参数,FAILOVER_RETRIES则默认为5次重试。
FAILOVER_DELAY 默认值:1秒 该参数指定在连接尝试之间等待的时间(以秒为单位)。
若配置了FAILOVER_RETRIES参数,FAILOVER_DELAY则默认为1秒。

Note:

当TAF注册回调函数后,FAILOVER_RETRIES和FAILOVER_DELAY参数将会失效。

# 配置方式

# 方式一

  1. 进入$YASDB_HOME/client路径,在yasc_service.ini文件中新增配置,配置示例如下:
    REMOTE = 192.168.1.2:1688, 192.168.1.3:1688, 192.168.1.4:1688 ? FAILOVER = ON & FAILOVER_TYPE = SESSION & FAILOVER_METHOD = BASIC & FAILOVER_RETRIES = 10 & FAILOVER_DELAY = 2
    
  2. 使用REMOTE数据源连接:
    const YacChar* gSrvStr = "REMOTE";
    YAC_CALL(yacConnect(conn, gSrvStr, YAC_NULL_TERM_STR, user, YAC_NULL_TERM_STR, pwd, YAC_NULL_TERM_STR));
    

# 方式二

直接在连接URL中配置:

const YacChar* gSrvStr = "192.168.1.2:1688, 192.168.1.3:1688, 192.168.1.4:1688 ? FAILOVER = ON & FAILOVER_TYPE = SESSION & FAILOVER_METHOD = BASIC & FAILOVER_RETRIES = 10 & FAILOVER_DELAY = 2";
YAC_CALL(yacConnect(conn, gSrvStr, YAC_NULL_TERM_STR, user, YAC_NULL_TERM_STR, pwd, YAC_NULL_TERM_STR));

# TAF接口与结构

# 数据类型说明

数据类型 合法值 说明
YacTafResult YAC_TAF_SUCCESS:TAF回调函数返回执行成功
YAC_TAF_RETRY:TAF回调函数返回重试,仅当YacTafEvent为YAC_TAF_EVENT_ERROR时,回调函数返回的该值才有意义
YAC_TAF_ERROR:TAF回调函数返回执行失败,返回该值仅表示状态不影响TAF正常流程
TAF回调函数返回值
YacTafType YAC_TAF_TYPE_NONE:不使用透明故障转移
YAC_TAF_TYPE_SESSION:SESSION类型的故障转移
YAC_TAF_TYPE_SELECT:SELECT类型的故障转移
TAF类型
YacTafEvent YAC_TAF_EVENT_BEGIN:透明故障转移开始
YAC_TAF_EVENT_END:透明故障转移结束,成功恢复连接
YAC_TAF_EVENT_ABORT:透明故障转移失败,当前连接断开
YAC_TAF_EVENT_ERROR:透明故障转移失败,可以选择是否重试,即可选返回YAC_TAF_RETRY
TAF的时间节点
YacTafResult - TAF回调函数定义
YacTafCallbackStruct - TAF回调函数注册用上下文

# 数据类型结构

/* YACLI TAF CallBackSet */
typedef enum EnYacTafResult {
    YAC_TAF_SUCCESS = 0,
    YAC_TAF_RETRY = 25410,
    YAC_TAF_ERROR = -1,
} YacTafResult;

typedef enum EnYacTafType {
    YAC_TAF_TYPE_NONE = 0,
    YAC_TAF_TYPE_SESSION = 1,
    YAC_TAF_TYPE_SELECT = 2,
} YacTafType;

typedef enum EnYacTafEvent {
    YAC_TAF_EVENT_BEGIN = 0,
    YAC_TAF_EVENT_END = 1,
    YAC_TAF_EVENT_ABORT = 2,
    YAC_TAF_EVENT_ERROR = 3,
} YacTafEvent;

typedef YacTafResult (*YacTafCallback)(YacHandle hConn, YacHandle hEnv, YacPointer tafCtx, YacTafType tafType, YacTafEvent tafEvent);

typedef struct {
    YacTafCallback tafCallbackFunc;
    YacPointer     tafCtx;
} YacTafCallbackStruct;

# TAF使用示例

# 普通TAF示例

YacResult testConnect()
{
    const YacChar* gSrvStr = "192.168.1.2:1688?FAILOVER = ON & FAILOVER_TYPE = SESSION & FAILOVER_RETRIES = 10 & FAILOVER_DELAY = 1";
    const YacChar* user = "sys";
    const YacChar* pwd = "password";
 
    YacHandle env;
    YacHandle conn;
    YAC_CALL(yacAllocHandle(YAC_HANDLE_ENV, NULL, &env));
    YAC_CALL(yacAllocHandle(YAC_HANDLE_DBC, env, &conn));
    YAC_CALL(yacConnect(conn, gSrvStr, YAC_NULL_TERM_STR, user, YAC_NULL_TERM_STR, pwd, YAC_NULL_TERM_STR));
    printf("connected!");
 
    YacHandle stmt;
    YAC_CALL(yacAllocHandle(YAC_HANDLE_STMT, conn, &stmt));
    // 如果此处发生网络中断,那么下一次的conn请求会触发TAF重连,接下来的请求会成功
    YAC_CALL(yacDirectExecute(stmt, "select 1 from dual", YAC_NULL_TERM_STR));
    YAC_CALL(yacDirectExecute(stmt, "select 1 from dual", YAC_NULL_TERM_STR));
 
    YAC_CALL(yacFreeHandle(YAC_HANDLE_STMT, stmt));
    yacDisconnect(conn);
    YAC_CALL(yacFreeHandle(YAC_HANDLE_DBC, conn));
    YAC_CALL(yacFreeHandle(YAC_HANDLE_ENV, env));
 
    return YAC_SUCCESS;
}

# TAF回调函数使用示例

YacTafResult tafCb(YacHandle hConn, YacHandle hEnv, YacPointer tafCtx, YacTafType tafType, YacTafEvent tafEvent) 
{
    // 该回调函数控制TAF重连次数不超过2次,包括:
    // TAF首次尝试重连+发生YAC_TAF_EVENT_ERROR后返回YAC_TAF_RETRY指定重试
    YacUint32* intRetry = tafCtx;
    if (tafEvent == YAC_TAF_EVENT_BEGIN) {
        *intRetry = 0;
    }

    printf("TAF callback: %d, %d\n", tafType, tafEvent);
    if (tafEvent == YAC_TAF_EVENT_ERROR && *intRetry < 1) {
        (*intRetry)++;
        return YAC_TAF_RETRY;
    }
    return YAC_TAF_SUCCESS;
}

YacResult testConnect1WithCallBack()
{
    const YacChar* gSrvStr = "192.168.1.2:1688?failover = on & FAILOVER_TYPE = SESSION";
    const YacChar* user = "sys";
    const YacChar* pwd = "password";

    YacHandle env;
    YacHandle conn;
    YAC_CALL(yacAllocHandle(YAC_HANDLE_ENV, NULL, &env));
    YAC_CALL(yacAllocHandle(YAC_HANDLE_DBC, env, &conn));

    YacChar* buf = malloc(sizeof(YacUint32));
    if (buf == NULL) {
        return YAC_ERROR;
    }
    YacUint32* intRetry = (YacUint32*)buf;
    YacTafCallbackStruct cbStruct = { .tafCtx = intRetry,.tafCallbackFunc = tafCb };

    YAC_CALL(yacSetConnAttr(conn, YAC_ATTR_TAF_CALLBACK, (YacVoid*)&cbStruct, sizeof(YacTafCallbackStruct)));
    YAC_CALL(yacConnect(conn, gSrvStr, YAC_NULL_TERM_STR, user, YAC_NULL_TERM_STR, pwd, YAC_NULL_TERM_STR));
    printf("connected!");

    YacHandle stmt;
    YAC_CALL(yacAllocHandle(YAC_HANDLE_STMT, conn, &stmt));
    // 如果此处发生网络中断,那么下一次的conn请求会触发TAF重连,接下来的请求会成功
    YAC_CALL(yacDirectExecute(stmt, "select 1 from dual", YAC_NULL_TERM_STR));
    YAC_CALL(yacDirectExecute(stmt, "select 1 from dual", YAC_NULL_TERM_STR));
    
    YAC_CALL(yacFreeHandle(YAC_HANDLE_STMT, stmt));
    yacDisconnect(conn);
    YAC_CALL(yacFreeHandle(YAC_HANDLE_DBC, conn));
    YAC_CALL(yacFreeHandle(YAC_HANDLE_ENV, env));
    free(buf);

    return YAC_SUCCESS;
}