#透明故障转移

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

# TAF(Transparent Application Failover)配置

# 配置参数

参数名以及参数值不区分大小写。

参数名 合法值 说明
FAILOVER ON:开启透明故障转移
OFF:关闭透明故障转移(默认值)
该参数表示TAF是否开启。当故障发生的时候,TAF会链接到已配置的其他数据库节点。
FAILOVER_TYPE NONE:不使用透明故障转移(默认值)
SESSION:当用户的连接丢失,将自动为用户创建一个新会话。
SELECT:允许打开游标的用户在失败后继续获取它们。目前不支持该选项。
该参数指定故障转移的类型。
FAILOVER_METHOD BASIC:在故障转移时建立连接。此选项几乎不需要在故障转移时间之前对备份数据库服务器进行任何操作。(默认值)
PRECONNECT:预先建立连接。这提供了更快的故障转移,但要求备份实例能够支持来自每个受支持实例的所有连接。目前不支持该选项。
该参数决定了从主节点到备份节点的故障转移速度。
FAILOVER_RETRIES 默认值:5次 该参数指定故障转移后尝试连接的次数。如果FAILOVER_DELAY指定,FAILOVER_RETRIES则默认为五次重试。
FAILOVER_DELAY 默认值:1秒 该参数指定在连接尝试之间等待的时间(以秒为单位)。如果FAILOVER_RETRIES指定,FAILOVER_DELAY则默认为一秒。

PS:当TAF注册了回调函数后,FAILOVER_RETRIES、FAILOVER_DELAY参数将会失效。

# 配置方式

1、在yasc_service.ini文件中配置,配置示例如下:

REMOTE = 192.168.146.129:1688, 192.168.146.130:1688, 192.168.146.128:1688 ? FAILOVER = ON & FAILOVER_TYPE = SESSION & FAILOVER_METHOD = BASIC & FAILOVER_RETRIES = 10 & FAILOVER_DELAY = 2

PS:yasc_service.ini文件的配置路径在$YASDB_HOME/client下。

然后使用REMOTE数据源连接:

const YacChar* gSrvStr = "REMOTE";
YAC_CALL(yacConnect(conn, gSrvStr, YAC_NULL_TERM_STR, user, YAC_NULL_TERM_STR, pwd, YAC_NULL_TERM_STR));

2、直接在连接URL中配置:

const YacChar* gSrvStr = "192.168.146.129:1688, 192.168.146.130:1688, 192.168.146.128: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(Transparent Application Failover)接口与结构

# 数据类型说明

数据类型 合法值 说明
YacTafResult YAC_TAF_SUCCESS:TAF回调函数返回执行成功
YAC_TAF_RETRY:TAF回调函数返回重试(只有YacTafEvent为YAC_TAF_EVENT_ERROR时,回调函数返回YAC_TAF_RETRY才能生效)
YAC_TAF_ERROR:TAF回调函数返回执行失败
TAF回调函数返回值。(YAC_TAF_ERROR不影响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(Transparent Application Failover)使用示例

# 1、普通TAF示例

YacResult testConnect()
{
    const YacChar* gSrvStr = "127.0.0.1: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;
}

# 2、TAF回调函数使用实例

YacTafResult tafCb(YacHandle hConn, YacHandle hEnv, YacPointer tafCtx, YacTafType tafType, YacTafEvent tafEvent) 
{
    // 该回调函数控制TAF重连次数不超过2次,这里的2次包括:
    // TAF首次尝试重连的1次+发生YAC_TAF_EVENT_ERROR后返回YAC_TAF_RETRY指定的1次
    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.146.128: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;
}