Flink源码解析(十七)——Flink Task写数据过程解析

上篇随笔解析完Task启动过程,本篇开始解析Task写数据过程。Task写数据过程中涉及到RecordWriterOutput、RecordWriter、ChannelSelector、ResultPartition、ResultSubpartition、LocalBufferPool等组件,这些组件在之前随笔中或多或少都提及过,本篇随笔着重解析数据在这些组件之间的流转过程。

一、RecordWriterOutput

1、RecordWriterOutput结构解析,Output接口面向api层,负责向下游传递数据。由下图可知RecordWriterOutput类型包含recordWriter、serializationDelegate等重要成员变量,包含分区器信息的recordWriter主要面向流中Record元素,负责转发Output接收的元素到下游某一个子分区中。serializationDelegate负责序列化Record元素数据。

2、以flatMap(...) api为例,当Flink应用人员实现FlatMapFunction UDF函数时最后一步会调用collector.collect(...)方法向下游发送数据,此处collector继承于Output接口,上篇随笔讲到Output接口继承于Collector接口,并且在OperatorChain创建过程中每个算子都有一个Output成员实例来接收本算子产生的数据并向下游发送数据。当算子之间是不可Chain边时,Output成员的实际类型往往是RecordWriterOutput类型。推知下图StreamFlatMap算子在算子链生成过程中创建Output实例,并在open()方法调用时封装出collector成员。在Flink应用运行过程中触发processElement(...)方法处理并收集数据。

RecordWriterOutput将接收到的StreamRecord数据元素赋值给serializationDelegate成员,借助recordWriter将带有序列化操作的serializationDelegate分发到某一个子分区中。

以上是RecordWriterOutput向下游转发数据的高层抽象,下面继续分析RecordWriter的实现。

二、RecordWriter

1、RecordWriter结构解析,由下图可知,主要包含targetPartition、numberOfChannels、serializer、outputFlusher等成员变量,各个成员含义如下:

(1)、ResultPartitionWriter targetPartition:结果分区,每一个算子实例Task都包含一个结果分区,结果分区的数量即是算子的并行度。

(2)、int numberOfChannels:结果子分区个数,对应下游有多少个算子实例Task消费该Task实例的数据。

(3)、DataOutputSerializer serializer:数据输出视图,包含一个byte数组,负责序列化StreamRecord数据元素并顺序写入byte数组中,最后封装出一个ByteBuffer内存区域。

(4)、OutputFlusher outputFlusher:定时刷新器,是一个单独的线程实现。当上游数据产出较慢时,该线程负责以固定的时间间隔将已有的buffer数据发送到下游,避免下游算子等待过长时间。

(5)、ChannelSelector<T> channelSelector:RecordWriter子类ChannelSelectorRecordWriter中包含一个分区器成员,用来决定一个StreamRecord数据元素被分发到哪个结果子分区中。

2、RecordWriter数据输出。当RecordWriterOutput实例调用RecordWriter.emit(T record)方法时,转入到RecordWriter实例方法emit(T record,int targetSubpartition)中。由下图可知RecordWriter先将StreamRecord数据元素序列化写入到ByteBuffer,继而调用targetPartition.emitRecord(...)方法将ByteBuff缓冲区数据写入到具体的子分区实例。

三、数据分区器ChannelSelector

具体信息参考随笔《Flink源码解析(六)——数据分区解析 》

四、结果分区ResultPartition和结果子分区ResultSubpartition

1、ResultPartition结构解析,包含以下几个核心成员:

(1)、int partitionIndex:算子实例Task对应的结果分区下标。
(2)、ResultPartitionType partitionType:结果分区类型。Flink流式应用类型一般是PIPELINED、PIPELINED_BOUNDED,PIPELINED_BOUNDED代表数据写入速度有限制,当流数据出现背压时,任务不会在自己的Buffer里缓存大量的数据。
(3)、int numSubpartitions:结果分区包含的结果子分区个数。
(4)、BufferPool bufferPool:结果分区包含的缓冲池,用来存储分配到子分区的数据,类型是LocalBuffPool。ResultPartition写数据时,会向bufferPool申请buffer并写入数据。
(5)、SupplierWithException<BufferPool, IOException> bufferPoolFactory:缓冲池工厂类,用来创建bufferPool,类型是NetworkBufferPool。

ResultPartition子类BufferWritingResultPartition包含结果子分区数组。

2、结果子分区ResultSubpartition结构解析,包含以下几个核心成员:

(1)、buffers:用于缓存写入的数据,等待下游Task消费buffers里的数据。

(2)、PipelinedSubpartitionView readView:读视图,该结果子分区数据消费行为的封装。

3、接上小节,调用targetPartition.emitRecord(...)方法将ByteBuffer缓冲区数据写入到具体的子分区实例中,方法实现中会先从LocalBufferPool资源池中获取Buffer资源并将ByteBuffer数据写入。BufferBuilder底层用MemorySegment代表Buffer资源信息。如果ByteBuffer数据过大,一个BufferBuilder被写满后还有剩余数据,则将该BufferBuilder记为finish状态,继续申请新BufferBuilder实例,直到把剩余数据写完。

调用appendUnicastDataForNewRecord(...)方法,分配新的BufferBuilder实例并加入结果子分区buffers队列,最后将ByteBuffer数据写入到BufferBuilder实例中。

从LocalBufferPool中申请新的BufferBuilder资源。

将数据Buffer存放到PipelinedSubpartition的buffers队列中。

五、LocalBufferPool

1、LocalBufferPool结构解析,包含以下几个核心成员:

(1)、NetworkBufferPool networkBufferPool:全局网络缓存池,一个TaskManager只有一个NetworkBufferPool。LocalBufferPool中的Buffer资源来源于NetworkBufferPool。

(2)、int numberOfRequiredMemorySegments:当前LocalBufferPool最少能申请的Buffer资源。

(3)、availableMemorySegments:LocalBufferPool中当前可用的Buffer资源集合,从NetworkBufferPool获取但还没缓存数据的Buffer。Buffer的底层存储是MemorySegment。

(4)、int maxNumberOfMemorySegments:能从NetworkBufferPool申请的最大Buffer资源数量。

(5)、int numberOfRequestedMemorySegments:当前为止从NetworkBufferPool申请的Buffer资源。

2、LocalBufferPool创建过程:

在解析Task启动过程时有一步设置结果分区的操作,LocalBufferPool的创建就在该设置步骤中,如下图。

创建LocalBufferPool时有三个重要参数控制着每个LocalBufferPool的Buffer资源数,参数分别是Task对应的下游任务数即结果子分区数*每个结果子分区的Buffer数+额外分配的Buffer数量。每个结果子分区的Buffer数由参数taskmanager.network.memory.buffers-per-channel控制,额外分配的Buffer数量由参数taskmanager.network.memory.floating-buffers-per-gate控制。

3、我们回到前面requestBufferBuilder(...)方法,如下图看一下具体的过程:

从LocalBufferPool可用队列里获取MemorySegment资源。

 

posted @ 2024-03-03 14:22  有一个娃  阅读(502)  评论(0)    收藏  举报