文章目录
一. RecordWriter封装数据并发送到网络
1. 数据发送到网络的具体流程
RecordWriter对接入的StreamRecord数据进行序列化并等待下游任务消费的过程,整个过程细节如下。
StreamRecord通过RecordWriterOutput写入RecordWriter,并在RecordWriter中通过RecordSerializer组件将StreamRecord序列化为ByteBuffer数据格式。
RecordWriter
向ResultPartition申请BufferBuilder对象
,用于构建BufferConsumer对象,将序列化后的二进制数据存储在申请到的Buffer中。ResultPartition会向LocalBufferPool申请MemorySegment内存块,用于存储Buffer数据
。BufferBuilder中会不断接入ByteBuffer数据,
直到将BufferBuilder中的Buffer空间占满
,此时会申请新的BufferBuilder继续构建BufferConsumer数据集。Buffer构建完成后,会调用flushTargetPartition()方法,
让ResultPartition向下游输出数据
,此时会通知NetworkSequenceViewReader组件开始消费ResultSubPartition中的BufferConsumer对象。当BufferConsumer中Buffer数据被
推送到网络后
,回收BufferConsumer中的MemorySegment内存空间,继续用于后续的消息处理。
2. 源码层面
接下来我们从源码的角度了解RecordWriter具体处理数据的逻辑。在RecordWriterOutput中调用pushToRecordWriter方法将数据写出。
通过recordWriter.emit(serializationDelegate)方法,将数据元素发送到RecordWriter中进行处理。主要逻辑如下
- 序列化数据为ByteBuffer二进制数据,并缓存在SpanningRecordSerializer.serializationBuffer对象中。
- 将序列化器生成的中间数据复制到指定分区中,实际上就是将ByteBuffer数据复制到BufferBuiler对象中。
- 如果BufferBuiler中存储了完整的数据元素,就会清空序列化器的中间数据,因为序列化器中累积的数据不宜过大。
protected void emit(T record, int targetSubpartition) throws IOException {
checkErroneous();
targetPartition.emitRecord(serializeRecord(serializer, record), targetSubpartition);
if (flushAlways) {
targetPartition.flush(targetSubpartition);
}
}
protected void emit(T record, int targetChannel) throws IOException,
InterruptedException {
checkErroneous();
// 数据序列化
serializer.serializeRecord(record);
// 将序列化器中的数据复制到指定分区中
if (copyFromSerializerToTargetChannel(targetChannel)) {
// 清空序列化器
serializer.prune();
}
}
2.1. Serializer的实现逻辑
接着了解如何将序列化器中的数据转换成Buffer并存储到ResultPartiton中,最终将数据发送到下游。
a. SpanningRecordSerializer的实现
SpanningRecordSerializer实现将序列化后的BytesBuffer数据写入BufferBuilder。
SpanningRecordSerializer对象主要包含了Data