文章目录
分析源码:先串起流程,再分析代码架构实现。
本文主要讨论,chunjun 对flink sql连接器的顶层接口是如何实现的。
一. flink sql 从planning到runtime的具体实现要素
带着问题去思考
- 问题一:flink的InputFormatSourceFunction被谁创建?
- 问题二:flink如何调用InputFormatSourceFunction消费数据?
先了解flink sql连接器在catalog、planning、runtime时的调用逻辑,见我的文章:【源码改造】Flink-jdbc-connector源码简析+改造支持谓词下推中的二.1.1 。
接下来我们了解在planning时,数据源消费数据的逻辑是如何动态生成的,这里我们关注如下几个关键点:
- catalog将create table的元数据以及trans sql类型(insert into或lookup join)传到具体实现中:
- getScanRuntimeProvider
- getLookupRuntimeProvider
- 具体数据消费逻辑(open、run、close等)实现,由DtInputFormatSourceFunction封装具体的InputFormat实现(比如:JdbcInputFormatBuilder),这里的调用看如下jdbc连接器的实现:
- InputFormat的实现封装了数据源元数据的初始化、with下参数注入到消费策略进而实现消费逻辑特定化等逻辑。
@Override
public ScanRuntimeProvider getScanRuntimeProvider(ScanContext runtimeProviderContext) {
final RowType rowType = (RowType) physicalRowDataType.getLogicalType();
TypeInformation<RowData> typeInformation = InternalTypeInfo.of(rowType);
ResolvedSchema projectionSchema =
ResolvedSchema.physical(rowType.getFieldNames(), physicalRowDataType.getChildren());
JdbcInputFormatBuilder builder = this.builder;
...
jdbcConfig.setColumn(columnList);
builder.setRestoreKeyUtil(restoreKeyUtil);
...
// 这里通过DtInputFormatSourceFunction封装了jdbc的调用流程,具体数据流转流程实现由JdbcInputFormatBuilder实现
return ParallelSourceFunctionProvider.of(
new DtInputFormatSourceFunction<>(builder.finish(), typeInformation),
false,
jdbcConfig.getParallelism());
}
在running时,DtInputFormatSourceFunction串起具体InputFormat(比如JdbcInputFormat)实例的消费逻辑(open 、 run、close等),开启数据消费的生命周期。生命周期具体的行为见如下分析。
那这些生命周期方法是如何被调用的?(后续文章分析)
二. InputFormatSourceFunction的能力
InputFormatSourceFunction 是 SourceFunction 使用inputFormat实现类消费数据源数据的接口
InputFormat实现类是具体连接器实现open、read、close等数据源生命周期的具体实现。
InputFormatSourceFunction提供了如下方法,接着我们详细分析每个实现:
open run cancel close getFormat getInputSplits
1. open:数据源上层级别设置
open主要实现数据源正式消费数据之前做的行为,如下解释:
接着我们看具体实现逻辑
public void open(Configuration parameters) throws Exception {
//给具体(比如mysql连接器的inputformat)的inputformat设置context、与parameters
StreamingRuntimeContext context = (StreamingRuntimeContext) getRuntimeContext();
if (format instanceof RichInputFormat) {
((RichInputFormat) format).setRuntimeContext(context);
}
format.configure(parameters);
//获取InputSplitProvider用于提供InputSplit
provider = context.getInputSplitProvider();
serializer = typeInfo.createSerializer(getRuntimeContext().getExecutionConfig());
splitIterator = getInputSplits();
isRunning = splitIterator.hasNext