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,则重做日志是必需的。
相关推荐
api茶飘香1 小时前
守护应用边界:通过反射API实现安全的输入输出过滤
java·开发语言·python·安全·django·virtualenv·pygame
杀死一只知更鸟debug1 小时前
策略模式的小记
java·开发语言·策略模式
nice666601 小时前
CSS的基本语法
java·前端·css·visual studio code
梦想平凡2 小时前
源码搭建:从零开始实现程序的步骤解析
大数据·游戏·智能手机·源代码管理·游戏机
ever_up9733 小时前
EasyExcel的导入与导出及在实际项目生产场景的一下应用例子
java·开发语言·数据库
OkGogooXSailboat3 小时前
基于Flink的流式计算可视化开发实践之配置->任务生成->任务部署过程
大数据·flink
Mephisto.java3 小时前
【Hadoop|HDFS篇】NameNode和SecondaryNameNode
大数据·hadoop·hdfs
鹿子铭4 小时前
单线程Redis:Redis为什么这么快
数据库·redis
ok!ko4 小时前
设计模式之工厂模式(通俗易懂--代码辅助理解【Java版】)
java·开发语言·设计模式
学地理的小胖砸4 小时前
【GEE的Python API】
大数据·开发语言·前端·python·遥感·地图学·地理信息科学