Oracle-OracleConnectorTask

提示:OracleConnectorTask 是一个 Debezium 连接器的具体实现,用于捕获 Oracle 数据库中的数据变化,并将这些变化以 Kafka 消息的形式发布出去。

文章目录


前言

提示:OracleConnectorTask 是一个重要的组件,它负责从 Oracle 数据库中捕获数据变更事件,并将这些事件转换为 Kafka 消息,从而使得下游应用能够实时地消费这些变更事件。


提示:以下是本篇文章正文内容

一、核心功能

核心功能详细说明

1. 初始化和配置组件

  • 功能: 初始化和配置与 Oracle 数据库交互所需的组件。
  • 作用: 确保所有必需的服务和配置都准备好,以便能够开始捕获数据变更事件。

2. 创建协调器 (ChangeEventSourceCoordinator)

  • 功能: 创建一个协调器实例,用于管理数据变更事件的源。
  • 作用: 协调器负责管理数据变更事件的捕获过程,包括启动、停止以及处理事件流。

3. 检查归档日志目的地

  • 功能: 验证数据库归档日志目的地的有效性。
  • 作用: 确保归档日志配置正确,避免因配置问题导致无法捕获数据变更事件。

4. 获取心跳连接

  • 功能 : 创建一个新的 OracleConnection 实例用于心跳检测。
  • 作用: 心跳连接用于定期向数据库发送心跳信号,确保连接的有效性和可用性。

5. 轮询数据变更事件

  • 功能: 从队列中获取数据变更事件。
  • 作用 : 定期从事件队列中获取新的数据变更事件,并将其转换为 SourceRecord 对象。

6. 停止操作

  • 功能: 关闭 JDBC 连接及相关的资源。
  • 作用: 当任务不再需要运行时,释放占用的资源,避免资源泄露。

7. 获取所有配置字段

  • 功能 : 返回 OracleConnectorConfig 中的所有字段。
  • 作用: 提供所有配置项的列表,这有助于调试和配置验证。

8. 验证重做日志配置

  • 功能: 检查数据库是否处于归档模式。
  • 作用: 确认数据库配置支持 Debezium 所需的重做日志功能,以保证能够正确捕获数据变更事件。

二、代码分析

c 复制代码
// 创建并配置一个协调器实例,用于管理数据变更事件的源
ChangeEventSourceCoordinator<OraclePartition, OracleOffsetContext> coordinator = new ChangeEventSourceCoordinator<>(
        previousOffsets, // 已有的偏移量信息
        errorHandler, // 错误处理策略
        OracleConnector.class, // 当前连接器的类类型
        connectorConfig, // 连接器配置
        new OracleChangeEventSourceFactory( // 创建 Oracle 数据变更事件源工厂
                connectorConfig, // 连接器配置
                connectionFactory, // 数据库连接工厂
                errorHandler, // 错误处理策略
                dispatcher, // 事件分发器
                clock, // 时钟服务
                schema, // 架构管理器
                jdbcConfig, // JDBC 配置
                taskContext, // 任务上下文
                streamingMetrics, // 流式度量指标
                snapshotterService), // 快照服务
        new OracleChangeEventSourceMetricsFactory(streamingMetrics), // 创建 Oracle 数据变更事件源度量指标工厂
        dispatcher, // 事件分发器
        schema, // 架构管理器
        signalProcessor, // 信号处理器
        notificationService, // 通知服务
        snapshotterService); // 快照服务

// 启动协调器
coordinator.start(taskContext, this.queue, metadataProvider);

// 返回协调器实例
return coordinator;
}

// 检查归档日志目的地的有效性
private void checkArchiveLogDestination(OracleConnection connection, String destinationName) {
    try {
        // 如果指定了归档目的地名称
        if (!Strings.isNullOrBlank(destinationName)) {
            // 验证指定的归档目的地是否有效
            if (!connection.isArchiveLogDestinationValid(destinationName)) {
                LOGGER.warn("Archive log destination '{}' may not be valid, please check the database.", destinationName);
            }
        } else {
            // 如果没有指定归档目的地名称
            // 检查是否存在多个有效的归档目的地
            if (!connection.isOnlyOneArchiveLogDestinationValid()) {
                LOGGER.warn("There are multiple valid archive log destinations. " +
                        "Please add '{}' to the connector configuration to avoid log availability problems.",
                        OracleConnectorConfig.ARCHIVE_DESTINATION_NAME.name());
            }
        }
    } catch (SQLException e) {
        // 如果检查过程中发生 SQL 异常
        throw new DebeziumException("Error while checking validity of archive log configuration", e);
    }
}

// 创建一个新的 OracleConnection 实例用于心跳检测
private OracleConnection getHeartbeatConnection(OracleConnectorConfig connectorConfig, JdbcConfiguration jdbcConfig) {
    // 创建 OracleConnection 实例
    final OracleConnection connection = new OracleConnection(jdbcConfig);
    // 如果指定了 PDB 名称,则设置会话到该 PDB
    if (!Strings.isNullOrBlank(connectorConfig.getPdbName())) {
        connection.setSessionToPdb(connectorConfig.getPdbName());
    }
    // 返回连接实例
    return connection;
}

// 轮询数据变更事件
@Override
public List<SourceRecord> doPoll() throws InterruptedException {
    // 从队列中获取数据变更事件
    List<DataChangeEvent> records = queue.poll();

    // 将数据变更事件转换为 SourceRecord 列表
    List<SourceRecord> sourceRecords = records.stream()
            .map(DataChangeEvent::getRecord)
            .collect(Collectors.toList());

    // 返回 SourceRecord 列表
    return sourceRecords;
}

// 停止操作
@Override
public void doStop() {
    // 尝试关闭 JDBC 连接
    try {
        if (jdbcConnection != null) {
            jdbcConnection.close();
        }
    } catch (SQLException e) {
        // 如果关闭 JDBC 连接时发生异常
        LOGGER.error("Exception while closing JDBC connection", e);
    }

    // 尝试关闭 JDBC bean registry 连接
    try {
        if (beanRegistryJdbcConnection != null) {
            beanRegistryJdbcConnection.close();
        }
    } catch (SQLException e) {
        // 如果关闭 JDBC bean registry 连接时发生异常
        LOGGER.error("Exception while closing JDBC bean registry connection", e);
    }

    // 关闭架构管理器
    if (schema != null) {
        schema.close();
    }
}

// 获取所有配置字段
@Override
protected Iterable<Field> getAllConfigurationFields() {
    // 返回 OracleConnectorConfig 中的所有字段
    return OracleConnectorConfig.ALL_FIELDS;
}

// 验证重做日志配置
private void validateRedoLogConfiguration(OracleConnectorConfig config, SnapshotterService snapshotterService) {
    // 检查数据库是否处于归档模式
    final boolean archivelogMode = jdbcConnection.isArchiveLogMode();
    // 如果数据库不是归档模式
    if (!archivelogMode) {
        // 如果重做日志对于当前配置是必需的
        if (redoLogRequired(config, snapshotterService)) {
            // 抛出异常,因为缺少归档模式会导致无法正常工作
            throw new DebeziumException("The Oracle server is not configured to use a archive log LOG_MODE, which is "
                    + "required for this connector to work properly. Change the Oracle configuration to use a "
                    + "LOG_MODE=ARCHIVELOG and restart the connector.");
        } else {
            // 发出警告,但继续执行(如果重做日志不是严格必需的)
            LOGGER.warn("Failed the archive log check but continuing as redo log isn't strictly required");
        }
    }
}

// 检查重做日志是否对于当前配置是必需的
private static boolean redoLogRequired(OracleConnectorConfig config, SnapshotterService snapshotterService) {
    // 如果配置要求流式传输或者事务快照边界模式为 ALL,则重做日志是必需的
    return snapshotterService.getSnapshotter().shouldStream() ||
            config.getLogMiningTransactionSnapshotBoundaryMode() == OracleConnectorConfig.TransactionSnapshotBoundaryMode.ALL;
}

OracleConnectorTask 类展示了良好的面向对象设计原则,特别是在封装方面。它将与 Oracle 数据库交互的逻辑封装在一个类中,提供了对外部友好的接口,如 doPoll()doStop() 方法,同时隐藏了内部实现细节。

组件化与依赖注入
  • 组件化 : 该类通过组合其他组件来完成复杂的任务,例如使用 ChangeEventSourceCoordinator 来管理数据变更事件的捕获过程。这种组件化的思想有助于提高代码的可读性和可维护性。

  • 依赖注入 : 通过构造函数和方法参数传递依赖,而不是在类内部创建这些依赖。例如,在创建 ChangeEventSourceCoordinator 时,它接收了许多外部组件作为参数。这种方式遵循了依赖倒置原则,提高了代码的灵活性和可测试性。

职责单一原则
  • 职责单一 : OracleConnectorTask 类的主要职责是管理与 Oracle 数据库的交互,包括捕获数据变更事件、处理这些事件并将它们转换为 Kafka 消息。它不承担与这些职责无关的其他功能,这体现了单一职责原则。
代码质量
  • 清晰的命名 : 类名、方法名和变量名都非常直观,易于理解。例如,checkArchiveLogDestinationvalidateRedoLogConfiguration 方法名清楚地表达了它们的功能。

  • 异常处理 : 该类在处理可能发生的异常时采用了明确的错误处理策略,例如在 checkArchiveLogDestination 方法中抛出 DebeziumException。这有助于提高系统的健壮性。

  • 模块化 : 通过将不同的功能分解成独立的方法,提高了代码的可读性和可维护性。例如,checkArchiveLogDestinationvalidateRedoLogConfiguration 方法都是独立的功能单元,易于理解和测试。


总结

  1. 配置对象 (connectorConfig): 存储连接器配置信息的对象。
  2. 事件队列 (queue): 用于存储待处理的数据变更事件的队列。
  3. 错误处理器 (errorHandler): 处理捕获过程中出现的错误。
  4. 架构管理器 (schema): 用于管理数据库架构的对象。
  5. 通知服务 (notificationService): 用于发送通知的服务。
  6. 信号处理器 (signalProcessor): 用于处理信号的处理器。
  7. 快照服务 (snapshotterService): 用于执行快照操作的服务。
  8. JDBC 连接 (jdbcConnection): 与 Oracle 数据库通信的 JDBC 连接。
  9. JDBC 配置 (jdbcConfig): 存储 JDBC 连接配置信息的对象。
  10. 任务上下文 (taskContext): 包含任务相关信息的上下文对象。
  11. 时钟服务 (clock): 提供时间服务的对象。
  12. 分发器 (dispatcher): 用于分发数据变更事件的对象。
  13. 元数据提供者 (metadataProvider): 提供元数据信息的对象。
  14. 主题命名策略 (topicNamingStrategy): 用于确定 Kafka 主题名称的策略。
  15. 元数据提供者 (metadataProvider): 提供元数据信息的对象。
  16. 度量指标 (streamingMetrics): 用于收集度量指标的对象。

方法

ChangeEventSourceCoordinator<OraclePartition, OracleOffsetContext> coordinator
  • 功能 : 创建一个 ChangeEventSourceCoordinator 实例,用于管理数据变更事件的源。
  • 作用: 协调器负责管理数据变更事件的捕获过程,包括启动、停止以及处理事件流。
private void checkArchiveLogDestination(OracleConnection connection, String destinationName)
  • 功能: 验证数据库归档日志目的地的有效性。
  • 作用: 确保归档日志配置正确,避免因配置问题导致无法捕获数据变更事件。
private OracleConnection getHeartbeatConnection(OracleConnectorConfig connectorConfig, JdbcConfiguration jdbcConfig)
  • 功能 : 创建一个新的 OracleConnection 实例用于心跳检测。
  • 作用: 心跳连接用于定期向数据库发送心跳信号,确保连接的有效性和可用性。
@Override public List<SourceRecord> doPoll() throws InterruptedException
  • 功能: 从队列中获取数据变更事件。
  • 作用 : 定期从事件队列中获取新的数据变更事件,并将其转换为 SourceRecord 对象。
@Override public void doStop()
  • 功能: 关闭 JDBC 连接及相关的资源。
  • 作用: 当任务不再需要运行时,释放占用的资源,避免资源泄露。
@Override protected Iterable<Field> getAllConfigurationFields()
  • 功能 : 返回 OracleConnectorConfig 中的所有字段。
  • 作用: 提供所有配置项的列表,这有助于调试和配置验证。
private void validateRedoLogConfiguration(OracleConnectorConfig config, SnapshotterService snapshotterService)
  • 功能: 检查数据库是否处于归档模式。
  • 作用: 确认数据库配置支持 Debezium 所需的重做日志功能,以保证能够正确捕获数据变更事件。
private static boolean redoLogRequired(OracleConnectorConfig config, SnapshotterService snapshotterService)
  • 功能: 检查重做日志是否对于当前配置是必需的。
  • 作用: 如果配置要求流式传输或者事务快照边界模式为 ALL,则重做日志是必需的。
相关推荐
不爱学英文的码字机器2 分钟前
数字孪生的浪潮:从虚拟镜像到现实世界的 IT 变革
大数据·python
编程在手天下我有4 分钟前
Redis 数据类型全览:特性、场景与操作实例
数据库·redis·数据结构与算法
WuWuII5 分钟前
gateway
java·gateway
左灯右行的爱情8 分钟前
缓存并发更新的挑战
jvm·数据库·redis·后端·缓存
kaamelai11 分钟前
Kaamel视角下的MCP安全最佳实践
大数据·人工智能·安全
浩宇软件开发11 分钟前
Android开发,实现一个简约又好看的登录页
android·java·android studio·android开发
南客先生18 分钟前
多级缓存架构设计与实践经验
java·面试·多级缓存·缓存架构
anqi2721 分钟前
如何在 IntelliJ IDEA 中编写 Speak 程序
java·大数据·开发语言·spark·intellij-idea
m0_7401546727 分钟前
maven相关概念深入介绍
java·maven
fanTuanye40 分钟前
Spring-全面详解(学习总结)
java·spring·ssm框架