提示:MySqlConnectorTask 类是 Debezium 中用于捕捉 MySQL 数据库变更事件的一个核心组件。它的主要职责是在运行时处理 MySQL 数据库中的表结构变化和数据变更事件,并将这些事件转换为 Kafka 消息,以便下游系统(如 Apache Kafka)能够消费这些事件。
前言
提示:MySqlConnectorTask 类是
Debezium 一个重要的组件,它负责初始化和管理 MySQL 变更事件的处理流程,包括配置、错误处理、事件队列管理、事件分发、监控和资源清理等功能。
提示:以下是本篇文章正文内容
一、核心功能
核心功能详细说明
-
异常处理:
- 捕获
SQLException
异常,并将其封装为DebeziumException
抛出,确保上层能够处理这种类型的异常。
- 捕获
-
检查偏移量:
- 如果之前没有保存的偏移量(
previousOffset
),则意味着需要重新执行一次全量快照(snapshot)。 - 如果存在偏移量,则记录日志表明找到了之前的偏移量。
- 如果之前没有保存的偏移量(
-
初始化任务上下文:
- 创建
MySqlTaskContext
对象,用于存储连接器的配置和模式信息。
- 创建
-
设置事件队列:
- 创建
ChangeEventQueue
对象,用于缓存来自MySQL的变更事件。 - 配置队列的参数,如轮询间隔、最大批大小、最大队列大小等。
- 创建
-
错误处理:
- 创建
MySqlErrorHandler
对象,用于处理在捕获和处理变更事件过程中可能出现的错误。
- 创建
-
元数据提供者:
- 初始化
BinlogEventMetadataProvider
对象,用于提供二进制日志(binlog)事件的元数据。
- 初始化
-
信号处理器:
- 创建
SignalProcessor
对象,用于处理MySQL中的信号事件,比如心跳事件。
- 创建
-
重置偏移量:
- 调用
resetOffset
方法来根据配置和信号处理器重置偏移量。
- 调用
-
事件分发器:
- 创建
EventDispatcher
对象,用于将MySQL的变更事件分发到Kafka主题中。
- 创建
-
流变更事件源度量:
- 创建
MySqlStreamingChangeEventSourceMetrics
对象,用于收集和报告关于流变更事件源的度量信息。
- 创建
-
通知服务:
- 创建
NotificationService
对象,用于处理来自MySQL的特定通知事件。
- 创建
-
变更事件源协调器:
- 创建
ChangeEventSourceCoordinator
对象,这是整个变更事件捕获过程的核心组件。 - 它负责管理变更事件的捕获、处理和分发过程。
- 协调器启动后,会开始监听MySQL的变更事件,并通过队列和事件分发器将这些事件发送出去。
- 创建
-
辅助方法:
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
设计思路
- 职责明确:该类的主要职责是初始化和启动变更事件源协调器,它不直接处理具体的业务逻辑,而是负责组织和协调各个组件。
- 依赖注入:通过构造方法和方法参数传递配置和依赖,提高了模块间的解耦。
- 单例模式:虽然代码片段没有明确展示,但从其职责来看,此类很适合作为单例使用,确保在整个应用中只有一个实例来管理变更事件源协调器。
- 组合而非继承:通过组合多个对象(如
MySqlConnectorConfig
、MySqlValueConverters
等)来实现功能,而不是通过继承扩展行为,这符合面向对象设计原则中的“组合优于继承”。
代码优点
- 清晰的职责划分:每个方法都有明确的目的,易于理解和维护。
- 良好的可配置性:通过
Configuration
对象来配置不同的参数,使得系统的灵活性大大提高。 - 优雅的异常处理:在可能抛出异常的地方使用了适当的异常处理机制,增强了程序的健壮性。
- 模块化设计:通过创建不同的组件(例如连接工厂、偏移量管理器等),这些组件可以独立开发和测试,降低了整体复杂度。
- 良好的扩展性:通过接口和抽象类的使用,为未来添加新功能提供了便利,比如通过不同的
TopicNamingStrategy
或SchemaNameAdjuster
策略。
启发
- 面向对象设计的重要性:通过合理地设计类和它们之间的关系,可以提高代码的可读性和可维护性。
- 依赖注入:依赖注入不仅有助于降低类之间的耦合度,还便于单元测试。
- 关注点分离:将不同的功能模块化,并让每个类专注于单一职责,可以使代码更加清晰和易于理解。
- 异常处理:合理的异常处理可以增强程序的健壮性,减少运行时错误的发生。
- 配置与代码分离:通过配置文件或对象来管理配置,可以避免硬编码,使应用程序更具灵活性。
总结
提示:MySqlConnectorTask是一个MySQL数据库变更数据捕获(CDC)任务的核心初始化类,它负责整个任务的初始化配置、服务注册、状态管理、错误处理、事件分发和变更事件源协调等工作。通过面向对象的设计,这个类实现了高度的模块化和可维护性,确保了系统能够高效稳定地运行。