【Flink网络传输】ShuffleMaster与ShuffleEnvironment创建细节与提供的能力

本文围绕Flink展开,介绍了Taskmanager间数据传递细节,ResultPartition存储中间结果,InputChannel读取上游数据。还阐述了ShuffleService的设计与实现,以及在JobMaster中创建ShuffleMaster、在TaskManager中创建ShuffleEnvironment的过程,最后说明了基于ShuffleEnvironment创建ResultPartition和InputGate的具体步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一. Taskmanager之间传递数据细节

Flink作业最终会被转换为ExecutionGraph并拆解成Task,在TaskManager中调度并执行,Task实例之间会发生跨TaskManager节点的数据交换,尤其是在DataStream API中使用了物理分区操作的情况。

ResultPartition组件存放中间结果等待下游节点消费:

从ExecutionGraph到物理执行图的转换中可以看出,ExecutionVertex最终会被转换为Task实例运行,在ExecutionGraph中上游节点产生的数据被称为IntermediateResult,物理执行图对应ResultPartition组件。在ResultPartition组件中会根据分区的数量再细分为ResultSubPartition。在ResultSubPartition中主要有BufferConsumer队列,用于本地存储Buffer数据,供下游的Task节点消费使用。

InputChannel读取上游数据

对下游的Task实例来讲,主要依赖InputGate组件读取上游数据,在InputGate组件中InputChannel和上游的ResultSubPartition数量相同(发送逻辑是?起到shuffle的作用)。
因此RecordWriter向ResultPartition中的ResultSubPartition写入Buffer数据,就是在向下游的InputChannel写入数据,因为最终会从ResultSubPartition的队列中读取Buffer数据再经过TCP网络连接发送到对应的InputChannel中。

在这里插入图片描述

ResultPartition(存储中间结果集)和InputGate(读取中间结果集)组件的创建

TaskManager接收到JobManager的Task创建请求时,会根据TaskDeploymentDescriptor中的参数创建并初始化ResultPartition和InputGate组件。Task启动成功并开始接入数据后,使用ResultPartition和InputGate组件实现上下游算子之间的跨网络数据传输

ShuffleMaster管理ResultPartition和InputGate。

在TaskManager实例中,主要通过ShuffleEnvironment统一创建ResultPartition和InputGate组件。在JobMaster中也会创建ShuffleMaster统一管理和监控作业中所有的ResultPartition和InputGate组件

 

因此在介绍ResultPartition和InputGate之前,我们先了解一下ShuffleMaster和ShuffleEnvironment的主要作用和创建过程。

 

二. ShuffleService的设计与实现

如图,创建ShuffleMaster和ShuffleEnvironment组件主要依赖ShuffleServiceFactory实现。同时为了实现可插拔的ShuffleService服务,ShuffleServiceFactory的实现类通过Java SPI的方式加载到ClassLoader中,即通过ShuffleServiceLoader从配置文件中加载系统配置的ShuffleServiceFactory实现类,因此用户也可以自定义实现Shuffle服务。

基于SPI的方式加载ShuffleServiceFactory

在JobManager内部创建JobManagerRunner实例的过程中会创建ShuffeServiceLoader,用于通过Java SPI服务的方式加载配置的ShuffleServiceFactory,同时在TaskManager的TaskManagerServices中创建ShuffeServiceLoader并加载ShuffleServiceFactory。

ShuffleServiceFactory提供了创建ShuffleMaster和ShuffleEnvironment的能力
ShuffleServiceFactory接口定义中包含创建ShuffleMaster和ShuffleEnvironment的方法。Flink提供了基于Netty通信框架实现的NettyShuffleServiceFactory,作为ShuffleServiceFactory接口的默认实现类。

ShuffleEnvironment组件提供了创建Task实例中ResultPartition和InputGate组件的方法,同时Flink中默认提供了NettyShuffleEnvironment实现。

ShuffleMaster组件实现了对ResultPartition和InputGate的注册功能

ShuffleMaster组件实现了对ResultPartition和InputGate的注册功能,同时每个作业都有ShuffleMaster管理当前作业的ResultPartition和InputGate等信息,Flink中提供了NettyShuffleMaster默认实现。

ShuffleService UML关系图

在这里插入图片描述

 

三. 在JobMaster中创建ShuffleMaster

创建ShuffleMaster,ShuffleEnvironment的大致过程

  • 通过ShuffleServiceFactory可以创建ShuffleMaster和ShuffleEnvironment服务,其中ShuffleMaster主要用在JobMaster调度和执行Execution时,维护当前作业中的ResultPartition信息,例如ResourceID、ExecutionAttemptID等
  • 紧接着JobManager会将ShuffleMaster创建的NettyShuffleDescriptor参数信息发送给对应的TaskExecutor实例,在TaskExecutor中就会基于NettyShuffleDescriptor的信息,通过ShuffleEnvironment组件创建ResultPartition、InputGate等组件。

分配slot资源,并将分区信息注册到ShuffleMaster中

如代码清单,在JobMaster开始向Execution分配Slot资源时,会通过分配的Slot计算资源获取TaskManagerLocation信息,然后调用Execution.registerProducedPartitions()方法将分区信息注册到ShuffleMaster中。

CompletableFuture<Execution> allocateResourcesForExecution(
      SlotProviderStrategy slotProviderStrategy,
      LocationPreferenceConstraint locationPreferenceConstraint,
      @Nonnull Set<AllocationID> allPreviousExecutionGraphAllocationIds) {
   
   
   return allocateAndAssignSlotForExecution(
      slotProviderStrategy,
      locationPreferenceConstraint,
      allPreviousExecutionGraphAllocationIds)
      .thenCompose(slot -> registerProducedPartitions(slot.getTaskManagerLocation()));
}

Execution.registerProducedPartitions()方法逻辑如下。

  1. 创建ProducerDescriptor对象,其中包含了分区生产者的基本信息,例如网络连接地址和端口以及TaskManagerLocation信息
  2. 获取当前ExecutionVertex节点对应的IntermediateResultPartition信息,在IntermediateResultPartition结构中包含了ExecutionVertex、IntermediateResultPartitionID以及ExecutionEdge等逻辑分区信息。
  3. 遍历IntermediateResultPartition列表,将IntermediateResultPartition转换为PartitionDescriptor数据结构,然后调用ExecutionGraph的ShuffleMaster服务,将创建的PartitionDescriptor和ProducerDescriptor注册到ShuffleMaster服务中
  4. 根据ShuffleDescriptor创建ResultPartitionDeploymentDescriptor并添加到partitionRegistrations集合中。(producedPartitions信息会被TaskManager的ShuffleEnvironment用于创建ResultPartition和InputGate等组件。
static CompletableFuture<Map<IntermediateResultPartitionID, ResultPartitionDep
   loymentDescriptor>> registerProducedPartitions(
      ExecutionVertex vertex,
      TaskManagerLocation location,
      ExecutionAttemptID attemptId,
      boolean sendScheduleOrUpdateConsumersMessage) {
   
   
     // 创建ProducerDescriptor
   ProducerDescriptor producerDescriptor = 
       ProducerDescriptor.create(location, attemptId);
     // 获取当前节点的partition信息
   Collection<IntermediateResultPartition> partitions = 
       vertex.getProducedPartitions().values();
   Collection<CompletableFuture<ResultPartitionDeploymentDescriptor>> 
      partitionRegistrations =
      new ArrayList<>(partitions.size());
     // 向ShuffleMaster注册partition信息
   for (IntermediateResultPartition partition : partitions) {
   
   
      PartitionDescriptor partitionDescriptor = PartitionDescriptor.from(partition);
      int maxParallelism = getPartitionMaxParallelism(partition);
      // 调用ShuffleMaster注册partitionDescriptor和producerDescriptor
      CompletableFuture<? extends ShuffleDescriptor> shuffleDescriptorFuture = vertex
         .getExecutionGraph()
         .getShuffleMaster()
         .registerPartitionWithProducer(partitionDescriptor, producerDescriptor);
      Preconditions.checkState(shuffleDescriptorFuture.isDone(), 
         "ShuffleDescriptor future is incomplete.");
      // 创建ResultPartitionDeploymentDescriptor实例
      CompletableFuture<ResultPartitionDeploymentDescriptor> 
         partitionRegistration = 
          shuffleDescriptorFuture
         .thenApply(shuffleDescriptor -> new ResultPartitionDeploymentDescriptor(
            partitionDescriptor,
            shuffleDescriptor,
            maxParallelism,
            sendScheduleOrUpdateConsumersMessage));
      // 添加到partitionRegistrations集合中
      partitionRegistrations.add(partitionRegistration);
   }
   // 转换存储结构
   return FutureUtils.combineAll(partitionRegistrations).thenApply(rpdds -> {
   
   
      Map<IntermediateResultPartitionID, ResultPartitionDeploymentDescriptor> 
         producedPartitions =
         new LinkedHashMap<
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

roman_日积跬步-终至千里

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值