MySql-MySqlConnectorTask

提示:MySqlConnectorTask 类是 Debezium 中用于捕捉 MySQL 数据库变更事件的一个核心组件。它的主要职责是在运行时处理 MySQL 数据库中的表结构变化和数据变更事件,并将这些事件转换为 Kafka 消息,以便下游系统(如 Apache Kafka)能够消费这些事件。


前言

提示:MySqlConnectorTask 类是 Debezium 一个重要的组件,它负责初始化和管理 MySQL 变更事件的处理流程,包括配置、错误处理、事件队列管理、事件分发、监控和资源清理等功能。


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

一、核心功能

核心功能详细说明

  1. 异常处理:

    • 捕获SQLException异常,并将其封装为DebeziumException抛出,确保上层能够处理这种类型的异常。
  2. 检查偏移量:

    • 如果之前没有保存的偏移量(previousOffset),则意味着需要重新执行一次全量快照(snapshot)。
    • 如果存在偏移量,则记录日志表明找到了之前的偏移量。
  3. 初始化任务上下文:

    • 创建MySqlTaskContext对象,用于存储连接器的配置和模式信息。
  4. 设置事件队列:

    • 创建ChangeEventQueue对象,用于缓存来自MySQL的变更事件。
    • 配置队列的参数,如轮询间隔、最大批大小、最大队列大小等。
  5. 错误处理:

    • 创建MySqlErrorHandler对象,用于处理在捕获和处理变更事件过程中可能出现的错误。
  6. 元数据提供者:

    • 初始化BinlogEventMetadataProvider对象,用于提供二进制日志(binlog)事件的元数据。
  7. 信号处理器:

    • 创建SignalProcessor对象,用于处理MySQL中的信号事件,比如心跳事件。
  8. 重置偏移量:

    • 调用resetOffset方法来根据配置和信号处理器重置偏移量。
  9. 事件分发器:

    • 创建EventDispatcher对象,用于将MySQL的变更事件分发到Kafka主题中。
  10. 流变更事件源度量:

    • 创建MySqlStreamingChangeEventSourceMetrics对象,用于收集和报告关于流变更事件源的度量信息。
  11. 通知服务:

    • 创建NotificationService对象,用于处理来自MySQL的特定通知事件。
  12. 变更事件源协调器:

    • 创建ChangeEventSourceCoordinator对象,这是整个变更事件捕获过程的核心组件。
    • 它负责管理变更事件的捕获、处理和分发过程。
    • 协调器启动后,会开始监听MySQL的变更事件,并通过队列和事件分发器将这些事件发送出去。
  13. 辅助方法:

    • getValueConverters: 根据配置创建值转换器,用于处理不同类型的数据。
    • doPoll: 从队列中获取变更事件并转换为SourceRecord列表返回。
    • doStop: 在停止时关闭连接和其他资源。
    • getAllConfigurationFields: 获取所有配置字段。
    • getReadOnlyIncrementalSnapshotSignalOffset: 获取只读增量快照的信号偏移量。

二、代码分析

public ChangeEventSourceCoordinator<MySqlPartition, MySqlOffsetContext> start(Configuration configuration) {
        final Clock clock = Clock.system(); // 创建系统时钟实例
        final MySqlConnectorConfig connectorConfig = new MySqlConnectorConfig(configuration); // 创建连接器配置对象
        final TopicNamingStrategy<TableId> topicNamingStrategy = connectorConfig.getTopicNamingStrategy(MySqlConnectorConfig.TOPIC_NAMING_STRATEGY); // 获取主题命名策略
        final SchemaNameAdjuster schemaNameAdjuster = connectorConfig.schemaNameAdjuster(); // 获取模式名称调整器
        final MySqlValueConverters valueConverters = getValueConverters(connectorConfig); // 获取值转换器

        // DBZ-3238: 自动设置 "useCursorFetch" 为 true 当快照获取大小不为默认值 -1 时
        // 默认情况下不将整个结果集加载到内存中
        final Configuration config = configuration.edit() // 编辑配置
                .withDefault("database.responseBuffering", "adaptive") // 设置响应缓冲策略为自适应
                .withDefault("database.fetchSize", 10_000) // 设置默认的查询结果集大小
                .withDefault("database.useCursorFetch", connectorConfig.useCursorFetch()) // 设置是否使用游标获取
                .build(); // 构建配置对象

        MainConnectionProvidingConnectionFactory<BinlogConnectorConnection> connectionFactory = new DefaultMainConnectionProvidingConnectionFactory<>(() -> { // 创建主连接提供者工厂
            final MySqlConnectionConfiguration connectionConfig = new MySqlConnectionConfiguration(config); // 创建连接配置
            return new MySqlConnection(connectionConfig, MySqlFieldReaderResolver.resolve(connectorConfig)); // 返回一个新的 MySQL 连接
        });

        connection = connectionFactory.mainConnection(); // 获取主连接

        Offsets<MySqlPartition, MySqlOffsetContext> previousOffsets = getPreviousOffsets( // 获取先前的偏移量
                new MySqlPartition.Provider(connectorConfig, config), // 创建分区提供者
                new MySqlOffsetContext.Loader(connectorConfig)); // 创建偏移量加载器

        final boolean tableIdCaseInsensitive = connection.isTableIdCaseSensitive(); // 判断表ID是否区分大小写
        this.schema = new MySqlDatabaseSchema(connectorConfig, valueConverters, topicNamingStrategy, schemaNameAdjuster, tableIdCaseInsensitive); // 创建数据库模式

        // 手动注册 Bean
        beanRegistryJdbcConnection = connectionFactory.newConnection(); // 创建一个新的连接用于 Bean 注册
        connectorConfig.getBeanRegistry().add(StandardBeanNames.CONFIGURATION, config); // 注册配置 Bean
        connectorConfig.getBeanRegistry().add(StandardBeanNames.CONNECTOR_CONFIG, connectorConfig); // 注册连接器配置 Bean
        connectorConfig.getBeanRegistry().add(StandardBeanNames.DATABASE_SCHEMA, schema); // 注册数据库模式 Bean
        connectorConfig.getBeanRegistry().add(StandardBeanNames.JDBC_CONNECTION, beanRegistryJdbcConnection); // 注册 JDBC 连接 Bean
        connectorConfig.getBeanRegistry().add(StandardBeanNames.VALUE_CONVERTER, valueConverters); // 注册值转换器 Bean
        connectorConfig.getBeanRegistry().add(StandardBeanNames.OFFSETS, previousOffsets); // 注册偏移量 Bean

        // 注册服务提供者
        registerServiceProviders(connectorConfig.getServiceRegistry()); 

        final SnapshotterService snapshotterService = connectorConfig.getServiceRegistry().tryGetService(SnapshotterService.class); // 获取快照服务
        final Snapshotter snapshotter = snapshotterService.getSnapshotter(); // 获取快照器

        validateBinlogConfiguration(snapshotter, connection); // 验证二进制日志配置

        // 如果二进制日志位置不可用,则需要重新执行快照
        if (validateSnapshotFeasibility(snapshotter, previousOffsets.getTheOnlyOffset(), connection)) { 
            previousOffsets.resetOffset(previousOffsets.getTheOnlyPartition()); // 重置偏移量
        }

        LOGGER.info("Closing connection before starting schema recovery"); // 日志输出:关闭连接以开始模式恢复

        try {
            connection.close(); // 关闭连接
        }
        catch (SQLException e) {
            throw new DebeziumException(e); // 抛出异常
        }

        MySqlOffsetContext previousOffset = previousOffsets.getTheOnlyOffset(); // 获取唯一的偏移量

        validateAndLoadSchemaHistory(connectorConfig, connection::validateLogPosition, previousOffsets, schema, snapshotter); // 验证并加载模式历史

        LOGGER.info("Reconnecting after finishing schema recovery"); // 日志输出:重新连接后完成模式恢复

        try {
            connection.setAutoCommit(false); // 设置自动提交为 false
        }
        catch (SQLException e) {
            throw new DebeziumException(e); // 抛出异常
        }

        // 如果二进制日志位置不可用,则需要重新执行快照
        if (previousOffset == null) {
            LOGGER.info("No previous offset found"); // 日志输出:未找到先前的偏移量
        }
        else {
            LOGGER.info("Found previous offset {}", previousOffset); // 日志输出:找到先前的偏移量
        }

        taskContext = new MySqlTaskContext(connectorConfig, schema); // 创建任务上下文

        // 设置任务记录队列
        this.queue = new ChangeEventQueue.Builder<DataChangeEvent>() // 创建变更事件队列构建器
                .pollInterval(connectorConfig.getPollInterval()) // 设置轮询间隔
                .maxBatchSize(connectorConfig.getMaxBatchSize()) // 设置最大批次大小
                .maxQueueSize(connectorConfig.getMaxQueueSize()) // 设置最大队列大小
                .maxQueueSizeInBytes(connectorConfig.getMaxQueueSizeInBytes()) // 设置最大队列大小(以字节为单位)
                .loggingContextSupplier(() -> taskContext.configureLoggingContext(CONTEXT_NAME)) // 设置日志上下文供应商
                .buffering() // 启用缓冲
                .build(); // 构建队列

        errorHandler = new MySqlErrorHandler(connectorConfig, queue, errorHandler); // 创建错误处理器

        final BinlogEventMetadataProvider metadataProvider = new BinlogEventMetadataProvider(); // 创建元数据提供者

        SignalProcessor<MySqlPartition, MySqlOffsetContext> signalProcessor = new SignalProcessor<>( // 创建信号处理器
                MySqlConnector.class, connectorConfig, Map.of(), // 类型、配置、额外配置
                getAvailableSignalChannels(), // 获取可用的信号通道
                DocumentReader.defaultReader(), // 使用默认文档读取器
                previousOffsets); // 使用先前的偏移量
        resetOffset(connectorConfig, previousOffset, signalProcessor); // 重置偏移量

        final Configuration heartbeatConfig = config; // 创建心跳配置
        final EventDispatcher<MySqlPartition, TableId> dispatcher = new EventDispatcher<>( // 创建事件分发器
                connectorConfig, // 连接器配置
                topicNamingStrategy, // 主题命名策略
                schema, // 模式
                queue, // 变更事件队列
                connectorConfig.getTableFilters().dataCollectionFilter(), // 数据集合过滤器
                DataChangeEvent::new, // 数据变更事件构造器
                null, // 元数据提供者(这里为null,可能是遗留代码或未使用的参数)
                metadataProvider, // 元数据提供者
                connectorConfig.createHeartbeat( // 创建心跳
                        topicNamingStrategy, // 主题命名策略
                        schemaNameAdjuster, // 模式名称调整器
                        () -> new MySqlConnection( // 创建心跳连接
                                new MySqlConnectionConfiguration(heartbeatConfig), // 心跳连接配置
                                MySqlFieldReaderResolver.resolve(connectorConfig)), // 解析连接配置
                        new BinlogHeartbeatErrorHandler()), // 心跳错误处理器
                schemaNameAdjuster, // 模式名称调整器
                signalProcessor); // 信号处理器

        final MySqlStreamingChangeEventSourceMetrics streamingMetrics = new MySqlStreamingChangeEventSourceMetrics(taskContext, queue, metadataProvider); // 创建流变更事件源度量

        NotificationService<MySqlPartition, MySqlOffsetContext> notificationService = new NotificationService<>( // 创建通知服务
                getNotificationChannels(), // 获取通知通道
                connectorConfig, // 连接器配置
                SchemaFactory.get(), // 模式工厂
                dispatcher::enqueueNotification); // 注册通知

        ChangeEventSourceCoordinator<MySqlPartition, MySqlOffsetContext> coordinator = new ChangeEventSourceCoordinator<>( // 创建变更事件源协调器
                previousOffsets, // 前置偏移量
                errorHandler, // 错误处理器
                MySqlConnector.class, // 类型
                connectorConfig, // 连接器配置
                new MySqlChangeEventSourceFactory( // 变更事件源工厂
                        connectorConfig, // 连接器配置
                        connectionFactory, // 连接工厂
                        errorHandler, // 错误处理器
                        dispatcher, // 事件分发器
                        clock, // 时钟
                        schema, // 模式
                        taskContext, // 任务上下文
                        streamingMetrics, // 流变更事件源度量
                        queue, // 变更事件队列
                        snapshotterService), // 快照服务
                new MySqlChangeEventSourceMetricsFactory(streamingMetrics), // 变更事件源度量工厂
                dispatcher, // 事件分发器
                schema
设计思路
  1. 职责明确:该类的主要职责是初始化和启动变更事件源协调器,它不直接处理具体的业务逻辑,而是负责组织和协调各个组件。
  2. 依赖注入:通过构造方法和方法参数传递配置和依赖,提高了模块间的解耦。
  3. 单例模式:虽然代码片段没有明确展示,但从其职责来看,此类很适合作为单例使用,确保在整个应用中只有一个实例来管理变更事件源协调器。
  4. 组合而非继承:通过组合多个对象(如MySqlConnectorConfigMySqlValueConverters等)来实现功能,而不是通过继承扩展行为,这符合面向对象设计原则中的“组合优于继承”。
代码优点
  1. 清晰的职责划分:每个方法都有明确的目的,易于理解和维护。
  2. 良好的可配置性:通过Configuration对象来配置不同的参数,使得系统的灵活性大大提高。
  3. 优雅的异常处理:在可能抛出异常的地方使用了适当的异常处理机制,增强了程序的健壮性。
  4. 模块化设计:通过创建不同的组件(例如连接工厂、偏移量管理器等),这些组件可以独立开发和测试,降低了整体复杂度。
  5. 良好的扩展性:通过接口和抽象类的使用,为未来添加新功能提供了便利,比如通过不同的TopicNamingStrategySchemaNameAdjuster策略。
启发
  1. 面向对象设计的重要性:通过合理地设计类和它们之间的关系,可以提高代码的可读性和可维护性。
  2. 依赖注入:依赖注入不仅有助于降低类之间的耦合度,还便于单元测试。
  3. 关注点分离:将不同的功能模块化,并让每个类专注于单一职责,可以使代码更加清晰和易于理解。
  4. 异常处理:合理的异常处理可以增强程序的健壮性,减少运行时错误的发生。
  5. 配置与代码分离:通过配置文件或对象来管理配置,可以避免硬编码,使应用程序更具灵活性。

总结

提示:MySqlConnectorTask是一个MySQL数据库变更数据捕获(CDC)任务的核心初始化类,它负责整个任务的初始化配置、服务注册、状态管理、错误处理、事件分发和变更事件源协调等工作。通过面向对象的设计,这个类实现了高度的模块化和可维护性,确保了系统能够高效稳定地运行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值