TAF 是 Oracle 数据库的一项提供高可用性的功能。它使 PHP OCI8 应用程序能够在由于实例或网络故障导致数据库连接失败时自动重新连接到预配置的数据库。
在已配置的 Oracle 数据库系统中,当 PHP 应用程序检测到数据库实例已关闭或无法访问时,就会发生 TAF。它会在 Oracle » RAC 配置中建立与另一个节点的连接,一个热备数据库或相同的数据库实例本身。有关 OCI TAF 的更多信息,请参阅 » Oracle 调用接口程序员指南。
可以使用 oci_register_taf_callback() 注册应用程序回调。在故障转移期间,正常的应用程序处理会停止,并调用已注册的回调。回调会通知应用程序故障转移事件。如果故障转移成功,则会恢复正常的处理。如果故障转移中止,则应用程序中任何后续的数据库操作都将失败,因为没有可用的连接。
当连接故障转移到另一个数据库时,回调可以重置任何必要的连接状态,例如,如果数据库服务未启用 -failover_restore,则重播任何必要的 ALTER SESSION 命令。
可以通过调用 oci_unregister_taf_callback() 来删除应用程序回调。
可以在 PHP OCI8 端或数据库配置中配置 TAF。如果两者都已配置,则数据库端设置优先。
通过在连接描述符的 CONNECT_DATA 部分中包含 FAILOVER_MODE 参数来在 PHP OCI8(客户端)中配置 TAF。有关 TAF 客户端配置的更多信息,请参阅 » Oracle 数据库 Net 服务管理员指南 中的“配置透明应用故障转移”。
配置 TAF 以重新连接到同一数据库实例的 tnsnames.ora 示例
ORCL = (DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST = 127.0.0.1)(PORT = 1521)) (CONNECT_DATA = (SERVICE_NAME = orclpdb1) (FAILOVER_MODE = (TYPE = SELECT) (METHOD = BASIC) (RETRIES = 20) (DELAY = 15))))
或者,可以通过使用 » srvctl(对于 RAC)或 » DBMS_SERVICE.MODIFY_SERVICE 封装过程(对于单实例数据库)修改目标服务来在数据库端配置 TAF。
TAF 回调是在故障转移期间可以注册以调用的应用程序函数。在重新建立应用程序的连接期间,它会被多次调用。
当检测到连接丢失时,回调首先发生。这允许应用程序相应地应对即将到来的故障转移延迟。如果故障转移成功,则在连接重新建立并可用后调用回调。此时,应用程序可以重新同步会话设置并采取操作,例如通知用户已发生故障转移。如果故障转移不成功,则会发生回调以通知应用程序故障转移未发生并且连接不可用。
TAF 用户定义回调函数的接口如下所示
connection
通过 oci_register_taf_callback() 注册 TAF 回调的 Oracle 连接。在故障转移成功完成之前,连接无效。
event
故障转移事件指示故障转移的当前状态。
OCI_FO_BEGIN
指示故障转移已检测到连接丢失并且故障转移正在启动。
OCI_FO_END
指示故障转移已成功完成。
OCI_FO_ABORT
指示故障转移不成功,并且没有重试选项。
OCI_FO_ERROR
也指示故障转移不成功,但它使应用程序有机会处理错误并返回 OCI_FO_RETRY 以重试故障转移。
OCI_FO_REAUTH
指示 Oracle 用户已重新认证。
type
故障转移类型。这使回调能够知道应用程序已请求哪种类型的故障转移。通常值如下所示
OCI_FO_SESSION
指示用户仅请求会话故障转移。例如,如果用户的连接丢失,则会自动为用户在备份上创建一个新会话。此类型的故障转移不会尝试恢复 SELECT。
OCI_FO_SELECT
指示用户也请求 SELECT 故障转移。它允许具有打开游标的用户在故障后继续从中提取数据。
返回值
0
指示故障转移步骤应正常继续。
OCI_FO_RETRY
指示 Oracle 应再次尝试故障转移。如果在故障转移到新连接时发生错误,TAF 能够重试故障转移。通常,应用程序代码应休眠一段时间后再返回 OCI_FO_RETRY。
示例 #1 注册 TAF 回调
<?php
// 定义用户空间回调函数
class MyClass {
public static $retry_count;
public static function TAFCallback($conn, $event, $type)
{
switch ($event) {
case OCI_FO_BEGIN:
printf(" 正在进行故障转移... 请稍候\n");
printf(" 故障转移类型为:%s \n",
(($type==OCI_FO_SESSION) ? "SESSION"
:(($type==OCI_FO_SELECT) ? "SELECT" : "UNKNOWN!")));
self::$retry_count = 0;
break;
case OCI_FO_ABORT:
// 应用程序无法继续使用数据库
printf(" 故障转移中止。故障转移将不会进行。\n");
break;
case OCI_FO_END:
// 故障转移成功完成。通知用户发生了故障转移。
printf(" 故障转移结束... 正在恢复服务\n");
break;
case OCI_FO_REAUTH:
printf(" 用户故障转移... 正在恢复服务\n");
// 重放与此连接相关的任何 ALTER SESSION 命令
// 例如:oci_parse($conn, ‘ALTER SESSION …’) ;
break;
case OCI_FO_ERROR:
// 如果我们已经尝试了 20 次,则停止重试。
if (self::$retry_count >= 20)
return 0;
printf(" 收到故障转移错误。正在休眠...\n");
sleep(10);
self::$retry_count++;
return OCI_FO_RETRY; // 重试故障转移
break;
default:
printf("错误的故障转移事件:%d。\n", $event);
break;
}
return 0;
}
}
$fn_name = 'MyClass::TAFCallback';
$conn = oci_connect('hr', 'welcome', 'orcl');
$sysconn = oci_connect('system', 'oracle', 'orcl');
// 使用特权连接构建将启动故障转移的 SQL 语句
$sql = <<< 'END'
select unique 'alter system disconnect session '''||sid||','||serial#||''''
from v$session_connect_info
where sid = sys_context('USERENV', 'SID')
END;
$s = oci_parse($conn, $sql);
oci_execute($s);
$r = oci_fetch_array($s);
$disconnectssql = $r[0];
oci_register_taf_callback($conn, $fn_name); // 将 TAFCallback 注册到 Oracle TAF
print "正在解析用户查询\n";
$sql = "select systimestamp from dual";
$stmt = oci_parse($conn, $sql);
// 例如,如果此时发生连接丢失,oci_execute() 将
// 检测到丢失并开始故障转移。在故障转移期间,oci_execute() 将
// 多次调用 TAF 回调函数。如果故障转移成功,
// 将透明地创建新的连接,并且 oci_execute() 将像往常一样继续。连接会话设置可以在 TAF 回调
// 函数中重置。如果故障转移中止,oci_execute() 将返回错误,
// 因为没有可用的有效连接。
// 断开用户连接,启动故障转移
print "正在断开用户连接\n";
$discsql = oci_parse($sysconn, $disconnectssql);
oci_execute($discsql);
print "正在执行用户查询\n";
$e = oci_execute($stmt);
if (!$e) {
$m = oci_error($stmt);
trigger_error("无法执行语句:". $m['message'], E_USER_ERROR);
}
$row = oci_fetch_array($stmt);
print $row[0] . "\n";
// 使用新连接执行其他 SQL 语句(如果有效)
// $stmt = oci_parse($conn, . . .);
?>