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, . . .);
?>