Flink执行流程与源码分析(面试必问,建议收藏)

Flink架构及运行流程 Flink 架构及相关介绍 1.架构图 分层设计说明(相关术语解释) 物理部署层-deploy层 负责解决Flink的部署模式问题, 支持多种部署模式:本地部署、集群部署(Standalone/Yarn/Mesos)、云(GCE/EC2)以及kubernetes。 通过该层支持不同平台的部署,用户可以根据自身场景和需求选择使用对应的部署模式。 Runtime核心层 是Flink分布式计算框架的核心实现层,负责对上层不同接口提供基础服务。 支持分布式Stream作业的执行、JobGraph到Executio 阅读详情

 

Flink主要组件

 

作业管理器(JobManager)

(1) 控制一个应用程序执行的主进程,也就是说,每个应用程序 都会被一个不同的Jobmanager所控制执行

(2) Jobmanager会先接收到要执行的应用程序,这个应用程序会包括:作业图( Job Graph)、逻辑数据流图( ogical dataflow graph)和打包了所有的类、库和其它资源的JAR包。

(3) Jobmanager会把 Jobgraph转换成一个物理层面的 数据流图,这个图被叫做 “执行图”(Executiongraph),包含了所有可以并发执行的任务。Job Manager会向资源管理器( Resourcemanager)请求执行任务必要的资源,也就是 任务管理器(Taskmanager)上的插槽slot。一旦它获取到了足够的资源,就会将执行图分发到真正运行它们的 Taskmanager上。而在运行过程中Jobmanagera会负责所有需要中央协调的操作,比如说检查点(checkpoints)的协调。

任务管理器(Taskmanager)

(1) Flink中的工作进程。通常在 Flink中会有多个 Taskmanageria运行, 每个 Taskmanageri都包含了一定数量的插槽( slots)。插槽的数量限制了Taskmanageri能够执行的任务数量。

(2) 启动之后,  Taskmanager会向资源管理器注册它的插槽;收到资源管理器的指令后,  Taskmanageri就会将一个或者多个插槽提供给Jobmanageri调用。Jobmanager就可以向插槽分配任务( tasks)来执行了。

(3) 在执行过程中, 一个 Taskmanagera可以跟其它运行同一应用程序的Taskmanager交换数据。

资源管理器(Resource Manager)

(1) 主要负责管理任务管理器( Task Manager)的 插槽(slot)Taskmanger插槽是 Flink中定义的处理资源单元。

(2) Flink 为不同的环境和资源管理工具提供了不同资源管理器,比如YARNMesos、K8s,以及 standalone部署。

(3) 当 Jobmanager申请插槽资源时,  Resourcemanager会将有空闲插槽的Taskmanager?分配给Jobmanager。如果 Resourcemanagery没有足够的插槽来满足 Jobmanager的请求, 它还可以向资源提供平台发起会话,以提供启动 Taskmanager进程的容器。

分发器(Dispatcher)

(1) 可以跨作业运行,它为应用提交提供了REST接口。

(2)当一个应用被提交执行时,分发器就会启动并将应用移交给Jobmanage

(3) Dispatcher他会启动一个 WebUi,用来方便地 展示和监控作业执行的信息。

任务提交流程

  1. 提交应用

  2. 启动并提交应用

  3. 请求slots

  4. 任务启动

  5. 注册slots

  6. 发出提供slot的指令

  7. 提供slots

  8. 提交要在slots中执行的任务

  9. 交换数据

任务提交流程(YARN)

a. Flink任务提交后,Client向HDFS上传Flink的Jar包和配置

b. 随后向 Yarn ResourceManager提交任务ResourceManager分配 Container资源并通知对应的NodeManager启动

c. ApplicationMaster,ApplicationMaster 启动后加载Flink的Jar包和配置构建环境 

d. 然后启动JobManager , 之后ApplicationMaster 向ResourceManager 申请资源启动TaskManager

e. ResourceManager 分配 Container 资源后 , 由ApplicationMaster通知资源所在节点的NodeManager启动TaskManager

f. NodeManager 加载 Flink 的 Jar 包和配置构建环境并启动 TaskManager

g. TaskManager 启动后向  JobManager 发送心跳包,并等待 JobManager 向其分配任务。

源码分析--集群启动 JobManager 启动分析

JobManager 的内部包含非常重要的三大组件

  • WebMonitorEndpoint

  • ResourceManager

  • Dispatcher

入口,启动主类:StandaloneSessionClusterEntrypoint

// 入 口
StandaloneSessionClusterEntrypoint.main() ClusterEntrypoint.runClusterEntrypoint(entrypoint);
clusterEntrypoint.startCluster(); 
runCluster(configuration, pluginManager);

// 第一步:初始化各种服务
 /**
  * 初始化了 主节点对外提供服务的时候所需要的 三大核心组件启动时所需要的基础服务
  *  初始化服务,如 JobManager 的 Akka RPC 服务,HA 服务,心跳检查服务,metric service
  *  这些服务都是 Master 节点要使用到的一些服务
  *  1、commonRpcService:  基于 Akka 的 RpcService 实现。RPC 服务启动 Akka 参与者来接收从 RpcGateway 调用 RPC
  *  2、haServices:    提供对高可用性所需的所有服务的访问注册,分布式计数器和领导人选举
  *  3、blobServer:    负责侦听传入的请求生成线程来处理这些请求。它还负责创建要存储的目录结构 blob 或临时缓存它们
  *  4、heartbeatServices:  提供心跳所需的所有服务。这包括创建心跳接收器和心跳发送者。
  *  5、metricRegistry:   跟踪所有已注册的 Metric,它作为连接 MetricGroup 和 MetricReporter
  *  6、archivedExecutionGraphStore:   存储执行图ExecutionGraph的可序列化形式。
*/
initializeServices(configuration, pluginManager);

// 创建 DispatcherResourceManagerComponentFactory, 初始化各种组件的
工厂实例
// 其实内部包含了三个重要的成员变量:
// 创建 ResourceManager 的工厂实例
// 创建 Dispatcher 的工厂实例
// 创建 WebMonitorEndpoint 的工厂实例
createDispatcherResourceManagerComponentFactory(configuration);

// 创建 集群运行需要的一些组件:Dispatcher, ResourceManager 等
// 创 建 ResourceManager
// 创 建 Dispatcher
// 创 建 WebMonitorEndpoint
clusterComponent = dispatcherResourceManagerComponentFactory.create(...)

1. initializeServices():初始化各种服务

// 初 始 化 和 启 动 AkkaRpcService, 内 部 其 实 包 装 了 一 个 ActorSystem commonRpcService = AkkaRpcServiceUtils.createRemoteRpcService(...)

// 初始化一个负责 IO 的线程池
ioExecutor = Executors.newFixedThreadPool(...)
// 初始化 HA 服务组件,负责 HA 服务的是:ZooKeeperHaServices haServices = createHaServices(configuration, ioExecutor);

// 初始化 BlobServer 服务端
blobServer = new BlobServer(configuration, haServices.createBlobStore()); blobServer.start();

// 初始化心跳服务组件, heartbeatServices = HeartbeatServices heartbeatServices = createHeartbeatServices(configuration);

// 初始化一个用来存储 ExecutionGraph 的 Store, 实现是:
FileArchivedExecutionGraphStore
archivedExecutionGraphStore = createSerializableExecutionGraphStore(...)

2. createDispatcherResourceManagerComponentFactory(configuration)初始化了多组件的工厂实例

1、DispatcherRunnerFactory,默认实现:DefaultDispatcherRunnerFactory 

2、ResourceManagerFactory,默认实现:StandaloneResourceManagerFactory 

3、RestEndpointFactory,默认实现:SessionRestEndpointFactory

clusterComponent = dispatcherResourceManagerComponentFactory
    .create(configuration, ioExecutor, commonRpcService, haServices,
     blobServer, heartbeatServices, metricRegistry,
     archivedExecutionGraphStore,
     new RpcMetricQueryServiceRetriever(metricRegistry.getMetricQueryServiceRpcService()),
     this);

3. 创建 WebMonitorEndpoint

/*************************************************
  *  创建 WebMonitorEndpoint 实例, 在 Standalone 模式下:DispatcherRestEndpoint
  *  1、restEndpointFactory = SessionRestEndpointFactory
  *  2、webMonitorEndpoint = DispatcherRestEndpoint
  *  3、highAvailabilityServices.getClusterRestEndpointLeaderElectionService() = ZooKeeperLeaderElectionService
  *  当前这个 DispatcherRestEndpoint 的作用是:
  *  1、初始化的过程中,会一大堆的 Handler
  *  2、启动一个 Netty 的服务端,绑定了这些 Handler
  *  3、当 client 通过 flink 命令执行了某些操作(发起 restful 请求), 服务端由 webMonitorEndpoint 来执行处理
  *  4、举个例子: 如果通过 flink run 提交一个 Job,那么最后是由 webMonitorEndpoint 中的 JobSubmitHandler 来执行处理
  *  5、补充一个:job 由 JobSubmitHandler 执行完毕之后,转交给 Dispatcher 去调度执行
  */
 webMonitorEndpoint = restEndpointFactory.createRestEndpoint(
  configuration, dispatcherGatewayRetriever, resourceManagerGatewayRetriever,
  blobServer, executor, metricFetcher,
  highAvailabilityServices.getClusterRestEndpointLeaderElectionService(),
  fatalErrorHandler
 );

4. 创建 resourceManager

 /*************************************************
  *  创建 StandaloneResourceManager 实例对象
  *  1、resourceManager = StandaloneResourceManager
  *  2、resourceManagerFactory = StandaloneResourceManagerFactory
 */
 resourceManager = resourceManagerFactory.createResourceManager(
  configuration, ResourceID.generate(),
  rpcService, highAvailabilityServices, heartbeatServices,
  fatalErrorHandler, new ClusterInformation(hostname, blobServer.getPort()),
  webMonitorEndpoint.getRestBaseUrl(), metricRegistry, hostname
 );
protected ResourceManager<ResourceID> createResourceManager(
  Configuration configuration,
  ResourceID resourceId,
  RpcService rpcService,
  HighAvailabilityServices highAvailabilityServices,
  HeartbeatServices heartbeatServices,
  FatalErrorHandler fatalErrorHandler,
  ClusterInformation clusterInformation,
  @Nullable String webInterfaceUrl,
  ResourceManagerMetricGroup resourceManagerMetricGroup,
  ResourceManagerRuntimeServices resourceManagerRuntimeServices) {

 final Time standaloneClusterStartupPeriodTime = ConfigurationUtils.getStandaloneClusterStartupPeriodTime(configuration);

 /*************************************************
  *  注释: 得到一个 StandaloneResourceManager 实例对象
  */
 return new StandaloneResourceManager(
  rpcService,
  resourceId,
  highAvailabilityServices,
  heartbeatServices,
  resourceManagerRuntimeServices.getSlotManager(),
  ResourceManagerPartitionTrackerImpl::new,
  resourceManagerRuntimeServices.getJobLeaderIdService(),
  clusterInformation,
  fatalErrorHandler,
  resourceManagerMetricGroup,
  standaloneClusterStartupPeriodTime,
  AkkaUtils.getTimeoutAsTime(configuration)
 );

 }
 
/**
requestSlot():接受 solt请求
sendSlotReport(..): 将solt请求发送TaskManager
registerJobManager(...): 注册job管理者。 该job指的是 提交给flink的应用程序
registerTaskExecutor(...): 注册task执行者。
**/
public ResourceManager(RpcService rpcService, ResourceID resourceId, HighAvailabilityServices highAvailabilityServices,
  HeartbeatServices heartbeatServices, SlotManager slotManager, ResourceManagerPartitionTrackerFactory clusterPartitionTrackerFactory,
  JobLeaderIdService jobLeaderIdService, ClusterInformation clusterInformation, FatalErrorHandler fatalErrorHandler,
  ResourceManagerMetricGroup resourceManagerMetricGroup, Time rpcTimeout) {

 /*************************************************
  *  注释: 当执行完毕这个构造方法的时候,会触发调用 onStart() 方法执行
  */
 super(rpcService, AkkaRpcServiceUtils.createRandomName(RESOURCE_MANAGER_NAME), null);
 protected RpcEndpoint(final RpcService rpcService, final String endpointId) {
  this.rpcService = checkNotNull(rpcService, "rpcService");
  this.endpointId = checkNotNull(endpointId, "endpointId");

  /*************************************************
   *  注释:ResourceManager 或者 TaskExecutor 中的 RpcServer 实现
   *  以 ResourceManager 为例说明:
   *  启动 ResourceManager 的 RPCServer 服务
   *  这里启动的是 ResourceManager 的 Rpc 服务端。
   *  接收 TaskManager 启动好了而之后, 进行注册和心跳,来汇报 Taskmanagaer 的资源情况
   *  通过动态代理的形式构建了一个Server
   */
  this.rpcServer = rpcService.startServer(this);

5. 在创建resourceManager同级:启动任务接收器Starting Dispatcher

/*************************************************

 *  创建 并启动 Dispatcher
 *  1、dispatcherRunner = DispatcherRunnerLeaderElectionLifecycleManager
 *  2、dispatcherRunnerFactory = DefaultDispatcherRunnerFactory
 *  第一个参数:ZooKeeperLeaderElectionService
 *  -
 *  老版本: 这个地方是直接创建一个 Dispatcher 对象然后调用 dispatcher.start() 来启动
 *  新版本: 直接创建一个 DispatcherRunner, 内部就是要创建和启动 Dispatcher
 *  -
 *  DispatcherRunner 是对 Dispatcher 的封装。
 *  DispatcherRunner被创建的代码的内部,会创建 Dispatcher并启动
 */
log.debug("Starting Dispatcher.");
dispatcherRunner = dispatcherRunnerFactory.createDispatcherRunner(
 highAvailabilityServices.getDispatcherLeaderElectionService(), fatalErrorHandler,
 // TODO_ZYM 注释: 注意第三个参数
 new HaServicesJobGraphStoreFactory(highAvailabilityServices),
 ioExecutor, rpcService, partialDispatcherServices
);

Dispatcher 启动后,将会等待任务提交,如果有任务提交,则会经过submitJob(...)函数进入后续处理。

提交(一个Flink应用的提交必须经过三个graph的转换)

首先看下一些名词

StreamGraph

是根据用户通过 Stream API 编写的代码生成的最初的图。用来表示程序的拓扑结构。可以用一个 DAG 来表示),DAG 的顶点是 StreamNode,边是 StreamEdge,边包含了由哪个 StreamNode 依赖哪个 StreamNode。

  • StreamNode:用来代表 operator 的类,并具有所有相关的属性,如并发度、入边和出边等。

  • StreamEdge:表示连接两个StreamNode的边。

DataStream 上常见的 transformation 有 map、flatmap、filter等(见DataStream Transformation了解更多)。这些transformation会构造出一棵 StreamTransformation 树,通过这棵树转换成 StreamGraph

以map方法为例,看看源码

public <R> SingleOutputStreamOperator<R> map(MapFunction<T, R> mapper) {
  // 通过java reflection抽出mapper的返回值类型
  TypeInformation<R> outType = TypeExtractor.getMapReturnTypes(clean(mapper), getType(),
      Utils.getCallLocationName(), true);

  // 返回一个新的DataStream,SteramMap 为 StreamOperator 的实现类
  return transform("Map", outType, new StreamMap<>(clean(mapper)));
}

public <R> SingleOutputStreamOperator<R> transform(String operatorName, TypeInformation<R> outTypeInfo, OneInputStreamOperator<T, R> operator) {
  // read the output type of the input Transform to coax out errors about MissingTypeInfo
  transformation.getOutputType();

  // 新的transformation会连接上当前DataStream中的transformation,从而构建成一棵树
  OneInputTransformation<T, R> resultTransform = new OneInputTransformation<>(
      this.transformation,
      operatorName,
      operator,
      outTypeInfo,
      environment.getParallelism());

  @SuppressWarnings({ "unchecked", "rawtypes" })
  SingleOutputStreamOperator<R> returnStream = new SingleOutputStreamOperator(environment, resultTransform);

  // 所有的transformation都会存到 env 中,调用execute时遍历该list生成StreamGraph
  getExecutionEnvironment().addOperator(resultTransform);

  return returnStream;
}

map转换将用户自定义的函数MapFunction包装到StreamMap这个Operator中,再将StreamMap包装到OneInputTransformation,最后该transformation存到env中,当调用env.execute时,遍历其中的transformation集合构造出StreamGraph

JobGraph

(1) StreamGraph经过优化后生成了 JobGraph,提交给 JobManager 的数据结构。主要的优化为,将多个符合条件的节点 chain 在一起作为一个节点。

    

  • 将并不涉及到 shuffle 的算子进行合并。

  • 对于同一个 operator chain 里面的多个算子,会在同一个 task 中执行。

  • 对于不在同一个 operator chain 里的算子,会在不同的 task 中执行。

(2) JobGraph 用来由 JobClient 提交给 JobManager,是由顶点(JobVertex)、中间结果(IntermediateDataSet)和边(JobEdge)组成的 DAG 图。

(3) JobGraph 定义作业级别的配置,而每个顶点和中间结果定义具体操作和中间数据的设置。

JobVertex

JobVertex 相当于是 JobGraph 的顶点。经过优化后符合条件的多个StreamNode可能会chain在一起生成一个JobVertex,即一个JobVertex包含一个或多个operator,JobVertex的输入是JobEdge,输出是IntermediateDataSet。

IntermediateDataSet

JobVertex的输出,即经过operator处理产生的数据集。

JobEdge

job graph中的一条数据传输通道。source 是IntermediateDataSet,sink 是 JobVertex。即数据通过JobEdge由IntermediateDataSet传递给目标JobVertex。

(1) 首先是通过API会生成transformations,通过transformations会生成StreamGraph。

(2)将StreamGraph的某些StreamNode Chain在一起生成JobGraph,前两步转换都是在客户端完成。

(3)最后会将JobGraph转换为ExecutionGraph,相比JobGraph会增加并行度的概念,这一步是在Jobmanager里完成。

ExecutionJobVertex

ExecutionJobVertex一一对应JobGraph中的JobVertex

ExecutionVertex

一个ExecutionJobVertex对应n个ExecutionVertex,其中n就是算子的并行度。ExecutionVertex就是并行任务的一个子任务

Execution

Execution 是对 ExecutionVertex 的一次执行,通过 ExecutionAttemptId 来唯一标识。

IntermediateResult

在 JobGraph 中用 IntermediateDataSet 表示 JobVertex 的对外输出,一个 JobGraph 可能有 n(n >=0) 个输出。在 ExecutionGraph 中,与此对应的就是 IntermediateResult。每一个 IntermediateResult 就有 numParallelProducers(并行度) 个生产者,每个生产者的在相应的 IntermediateResult 上的输出对应一个 IntermediateResultPartition。IntermediateResultPartition 表示的是 ExecutionVertex 的一个输出分区

ExecutionEdge

ExecutionEdge 表示 ExecutionVertex 的输入,通过 ExecutionEdge 将 ExecutionVertex 和 IntermediateResultPartition 连接起来,进而在不同的 ExecutionVertex 之间建立联系。

ExecutionGraph的构建

  1. 构建JobInformation

  2. 构建ExecutionGraph

  3. 将JobGraph进行拓扑排序,获取sortedTopology顶点集合

// ExecutionGraphBuilder
 public static ExecutionGraph buildGraph(
  @Nullable ExecutionGraph prior,
  JobGraph jobGraph,
  ...) throws JobExecutionException, JobException {
  // 构建JobInformation
  
  // 构建ExecutionGraph
  
  // 将JobGraph进行拓扑排序,获取sortedTopology顶点集合
  List<JobVertex> sortedTopology = jobGraph.getVerticesSortedTopologicallyFromSources();
  
  executionGraph.attachJobGraph(sortedTopology);

  return executionGraph;
 }

  1. 构建ExecutionJobVertex,连接IntermediateResultPartition和ExecutionVertex

//ExecutionGraph
 public void attachJobGraph(List<JobVertex> topologiallySorted) throws JobException {
  for (JobVertex jobVertex : topologiallySorted) {
   // 构建ExecutionJobVertex
   ExecutionJobVertex ejv = new ExecutionJobVertex(
     this,
     jobVertex,
     1,
     maxPriorAttemptsHistoryLength,
     rpcTimeout,
     globalModVersion,
     createTimestamp);
   // 连接IntermediateResultPartition和ExecutionVertex
   ev.connectToPredecessors(this.intermediateResults);
 }
  
  
  // ExecutionJobVertex
 public void connectToPredecessors(Map<IntermediateDataSetID, IntermediateResult> intermediateDataSets) throws JobException {
  List<JobEdge> inputs = jobVertex.getInputs();
  
  for (int num = 0; num < inputs.size(); num++) {
   JobEdge edge = inputs.get(num);
   IntermediateResult ires = intermediateDataSets.get(edge.getSourceId());
   this.inputs.add(ires);
   int consumerIndex = ires.registerConsumer();
   
   for (int i = 0; i < parallelism; i++) {
    ExecutionVertex ev = taskVertices[i];
    ev.connectSource(num, ires, edge, consumerIndex);
   }
  }
 }

拆分计划(可执行能力)

// ExecutionVertex
 public void connectSource(int inputNumber, IntermediateResult source, JobEdge edge, int consumerNumber) {

  final DistributionPattern pattern = edge.getDistributionPattern();
  final IntermediateResultPartition[] sourcePartitions = source.getPartitions();

  ExecutionEdge[] edges;

  switch (pattern) {
   // 下游 JobVertex 的输入 partition 算法,如果是 forward 或 rescale 的话为 POINTWISE
   case POINTWISE:
    edges = connectPointwise(sourcePartitions, inputNumber);
    break;
   // 每一个并行的ExecutionVertex节点都会链接到源节点产生的所有中间结果IntermediateResultPartition
   case ALL_TO_ALL:
    edges = connectAllToAll(sourcePartitions, inputNumber);
    break;

   default:
    throw new RuntimeException("Unrecognized distribution pattern.");

  }

  inputEdges[inputNumber] = edges;
  for (ExecutionEdge ee : edges) {
   ee.getSource().addConsumer(ee, consumerNumber);
  }
 }


 private ExecutionEdge[] connectPointwise(IntermediateResultPartition[] sourcePartitions, int inputNumber) {
  final int numSources = sourcePartitions.length;
  final int parallelism = getTotalNumberOfParallelSubtasks();

  // 如果并发数等于partition数,则一对一进行连接
  if (numSources == parallelism) {
   return new ExecutionEdge[] { new ExecutionEdge(sourcePartitions[subTaskIndex], this, inputNumber) };
  }
  //  如果并发数大于partition数,则一对多进行连接
  else if (numSources < parallelism) {

   int sourcePartition;

   if (parallelism % numSources == 0) {
    int factor = parallelism / numSources;
    sourcePartition = subTaskIndex / factor;
   }
   else {
    float factor = ((float) parallelism) / numSources;
    sourcePartition = (int) (subTaskIndex / factor);
   }

   return new ExecutionEdge[] { new ExecutionEdge(sourcePartitions[sourcePartition], this, inputNumber) };
  }
  // 果并发数小于partition数,则多对一进行连接
  else {
   if (numSources % parallelism == 0) {
    int factor = numSources / parallelism;
    int startIndex = subTaskIndex * factor;

    ExecutionEdge[] edges = new ExecutionEdge[factor];
    for (int i = 0; i < factor; i++) {
     edges[i] = new ExecutionEdge(sourcePartitions[startIndex + i], this, inputNumber);
    }
    return edges;
   }
   else {
    float factor = ((float) numSources) / parallelism;

    int start = (int) (subTaskIndex * factor);
    int end = (subTaskIndex == getTotalNumberOfParallelSubtasks() - 1) ?
      sourcePartitions.length :
      (int) ((subTaskIndex + 1) * factor);

    ExecutionEdge[] edges = new ExecutionEdge[end - start];
    for (int i = 0; i < edges.length; i++) {
     edges[i] = new ExecutionEdge(sourcePartitions[start + i], this, inputNumber);
    }

    return edges;
   }
  }
 }


 private ExecutionEdge[] connectAllToAll(IntermediateResultPartition[] sourcePartitions, int inputNumber) {
  ExecutionEdge[] edges = new ExecutionEdge[sourcePartitions.length];

  for (int i = 0; i < sourcePartitions.length; i++) {
   IntermediateResultPartition irp = sourcePartitions[i];
   edges[i] = new ExecutionEdge(irp, this, inputNumber);
  }

  return edges;
 }

  1. 返回ExecutionGraph

TaskManager

TaskManager启动

public static void runTaskManager(Configuration configuration, ResourceID resourceId) throws Exception {
        //主要初始化一堆的service,并新建一个org.apache.flink.runtime.taskexecutor.TaskExecutor
  final TaskManagerRunner taskManagerRunner = new TaskManagerRunner(configuration,resourceId);
  //调用TaskExecutor的start()方法
        taskManagerRunner.start();
}

TaskExecutor :submitTask()

接着的重要函数是shumitTask()函数,该函数会通过AKKA机制,向TaskManager发出一个submitTask的消息请求,TaskManager收到消息请求后,会执行submitTask()方法。(省略了部分代码)。

public CompletableFuture<Acknowledge> submitTask(
   TaskDeploymentDescriptor tdd,
   JobMasterId jobMasterId,
   Time timeout) {

    jobInformation = tdd.getSerializedJobInformation().deserializeValue(getClass().getClassLoader());
    taskInformation = tdd.getSerializedTaskInformation().deserializeValue(getClass().getClassLoader());
   
   TaskMetricGroup taskMetricGroup = taskManagerMetricGroup.addTaskForJob(xxx);

   InputSplitProvider inputSplitProvider = new RpcInputSplitProvider(xxx);

   TaskManagerActions taskManagerActions = jobManagerConnection.getTaskManagerActions();
   CheckpointResponder checkpointResponder = jobManagerConnection.getCheckpointResponder();

   LibraryCacheManager libraryCache = jobManagerConnection.getLibraryCacheManager();
   ResultPartitionConsumableNotifier resultPartitionConsumableNotifier = jobManagerConnection.getResultPartitionConsumableNotifier();
   PartitionProducerStateChecker partitionStateChecker = jobManagerConnection.getPartitionStateChecker();

   final TaskLocalStateStore localStateStore = localStateStoresManager.localStateStoreForSubtask(
    jobId,
    tdd.getAllocationId(),
    taskInformation.getJobVertexId(),
    tdd.getSubtaskIndex());

   final JobManagerTaskRestore taskRestore = tdd.getTaskRestore();

   final TaskStateManager taskStateManager = new TaskStateManagerImpl(
    jobId,
    tdd.getExecutionAttemptId(),
    localStateStore,
    taskRestore,
    checkpointResponder);
            //新建一个Task
   Task task = new Task(xxxx);

   log.info("Received task {}.", task.getTaskInfo().getTaskNameWithSubtasks());

   boolean taskAdded;

   try {
    taskAdded = taskSlotTable.addTask(task);
   } catch (SlotNotFoundException | SlotNotActiveException e) {
    throw new TaskSubmissionException("Could not submit task.", e);
   }

   if (taskAdded) {
       //启动任务
    task.startTaskThread();

    return CompletableFuture.completedFuture(Acknowledge.get());
   } 

最后创建执行Task的线程,然后调用startTaskThread()来启动具体的执行线程,Task线程内部的run()方法承载了被执行的核心逻辑。

Task是执行在TaskExecutor进程里的一个线程,下面来看看其run方法

(1) 检测当前状态,正常情况为CREATED,如果是FAILED或CANCELING直接返回,其余状态将抛异常。

(2) 读取DistributedCache文件。

(3) 启动ResultPartitionWriter和InputGate。

(4) 向taskEventDispatcher注册partitionWriter。

(5) 根据nameOfInvokableClass加载对应的类并实例化。

(6) 将状态置为RUNNING并执行invoke方法。

public void run() {
        while (true) {
            ExecutionState current = this.executionState;
            invokable = loadAndInstantiateInvokable(userCodeClassLoader, nameOfInvokableClass);
            network.registerTask(this);
            Environment env = new RuntimeEnvironment(. . . . );
            invokable.setEnvironment(env);
            //  actual task core work
            if (!transitionState(ExecutionState.DEPLOYING, ExecutionState.RUNNING)) {
            }
            // notify everyone that we switched to running
            notifyObservers(ExecutionState.RUNNING, null);
            executingThread.setContextClassLoader(userCodeClassLoader);
            // run the invokable
            invokable.invoke();

            if (transitionState(ExecutionState.RUNNING, ExecutionState.FINISHED)) {
                notifyObservers(ExecutionState.FINISHED, null);
            }
            Finally{
                // free the network resources
                network.unregisterTask(this);
                // free memory resources
                if (invokable != null) {
                    memoryManager.releaseAll(invokable);
                }
                libraryCache.unregisterTask(jobId, executionId);
                removeCachedFiles(distributedCacheEntries, fileCache);

总结

整体的流程与架构可能三两张图或者三言两语就可以勾勒出画面,但是背后源码的实现是艰辛的。源码的复杂度和当初设计框架的抓狂感,我们只有想象。现在我们只是站在巨人的肩膀上去学习。

本篇的主题是"Flink架构与执行流程",做下小结,Flink on Yarn的提交执行流程:

1 Flink任务提交后,Client向HDFS上传Flink的Jar包和配置。

2 向Yarn ResourceManager提交任务。

3 ResourceManager分配Container资源并通知对应的NodeManager启动ApplicationMaster。

4 ApplicationMaster启动后加载Flink的Jar包和配置构建环境。

5 启动JobManager之后ApplicationMaster向ResourceManager申请资源启动TaskManager。

6 ResourceManager分配Container资源后,由ApplicationMaster通知资源所在节点

7 NodeManager加载Flink的Jar包和配置构建环境并启动TaskManager。

8 TaskManager启动后向JobManager发送心跳包,并等待JobManager向其分配任务。

 

大数据左右手

技术如同手中的水有了生命似的,汇聚在了一起。作为大数据开发工作者,致力于大数据技术的学习与工作,分享大数据原理、架构、实时、离线、面试与总结,分享生活思考与读书见解。总有适合你的那一篇。

关注公众号!!!

和我联系吧,加群交流大数据知识,一起成长~~~

Flink on Yarn的两种运行方式 Flink on Yarn的两种运行方式 第一种【yarn-session.sh(开辟资源)+flink run(提交任务)】 •启动一个一直运行的flink集群 •./bin/yarn-session.sh -n 2 -jm 1024 -tm 1024 [-d] •附着到一个已存在的flink yarn session •./bin/yarn-session.sh -id applic... 阅读详情

相关推荐

解读Flink双流Join

基本概念 双流Join是Flink面试的高频问题。一般情况下说明以下几点基本就可以cover了: Join大体分类只有两种:Window Join和Interval Join。Window Join又可以根据Window的类型细分出3种: Tumbling Window Join、Sliding Window Join、Session Widnow Join。 Windows类型的join都是利...

石权的博客 1万+

Flink执行过程

Flink执行流程

qq_42456324的博客 4445

Flink任务运行的详细步骤:从入门到精通

Flink 是一个高性能的流处理框架,适用于各种实时数据处理场景。通过本文的介绍,您应该已经了解了 Flink 任务运行的详细步骤。在实际应用中,Flink 可以帮助您更好地管理实时数据,提高数据处理能力和效率。无论您是数据处理的新手还是有一定经验的开发者,都应该掌握 Flink 的使用,以便在项目中发挥其强大的功能。希望本文的内容能对您有所帮助,让您的数据处理之路更加顺畅!

u013558123的博客 1658

flink任务执行流程剖析

背景 本文主要从源码角度对flink底层是如何将task分配到各个taskExecutor上执行,以及task执行的线程模型进行分析。会涉及到jobmaster如何将task分发到taskExecutor中,以及taskExecutor执行task的详细过程,以及task的mailBox线程模型。 JobMaster部署task TM启动Task线程 jobmaster主要通过以及分配的slot,获取到slot对应的taskmanager网管,然后提交task。TaskExecutor创建并启动Task线

weixin_45626756的博客 1922

Flink面试大全总结(全文6万字、110个知识点、160张图)

添加作者微信:threeknowbigdata,备注Flink,获取文章PDF版本 Hello,各位大数据学习爱好者,我是3分钟秒懂大数据公众号的作者土哥,目前在杭州某互联网大厂担任大数据算法工程师,组内专注于Flink流式计算组件以及AB融合技术,为了让更多朋友更清晰的了解流式计算组件,现在我以面试的方式为大家全面总结了Flink所涉及的知识点,全文总共6万字,涉及各种原理,以及源码分析,图片是一张张绘制而出,欢迎大家进行解读! 在互联网行业,我们都知道薪资结构跟岗位存在直接关系,如下面这幅关..

逆流而上Mr李 2711

Flink 原理与实现:Operator Chain原理

Flink的逻辑/执行计划优化,有一个很大的特点就是,会将多个operator,串在一起作为一个operator chain来执行。关于operator chain,在 Flink 原理与实现:理解 Flink 中的计算资源 中已经有了初步的介绍,在阅读本文之前,建议先阅读上文。本文将从源码上进一步分析,探究operator chain内部是如何实现的。 OperatorChain是在Strea......

微信搜:import_bigdata,大数据领域硬核原创作者 3673

Flink 面试题-sql

Flink SQL篇,SQL实操、Flink Hive、CEP、CDC、GateWay Flink源码篇,作业提交流程、作业调度流程、作业内部转换流程图 Flink核心篇,四大基石、容错机制、广播、反压、序列化、内存管理、资源管理 Flink基础篇,基本概念、设计理念、架构模型、编程模型、常用算子 1、Flink SQL有没有使用过? 2、Flink被称作流批一体,从哪个版本开始,真正实现流批一体的? 3、Flink SQL 使用哪种解析器? 4、Calcite主要功能包含哪些? 5、Fl.

张伟的专栏 2925

Flink1.10任务提交流程分析(一)

声明:本系列博客部分是根据SGG的视频整理而成,非常适合大家入门学习。部分文章是通过爬虫等技术手段采集的,目的是学习分享,如果有版权问题请留言,随时删除。 《2021年最新版大数据面试题全面开启更新》 Flink任务常见的提交方式通过flink run命令方式提交,如果我们想自己通过API方式实现任务提交,那么就需要了解flink run执行过程,本篇主要透过源码分析其提交流程。(注:基于1.10.1分析) 提交入口 查看bin/flink脚本可以看到提交入口类为:org.apache.flin

微信搜:import_bigdata,大数据领域硬核原创作者 446

flink源码分析_Flink 源码:StreamOperator 及其相关类介绍

本文是 Flink 源码解析系列开篇,好久没更新文章了,主要原因是换工作了,最近几个月业余时间一直在忙面试相关的一些东西。由于之前从事 Flink 业务开发,一般是线上排查问题或者做一些优化时,读一部分源码。虽然读过一些源码,但是不成系统,新公司入职一周多了,现在工作是从事 Flink 引擎开发(自己目前非常喜欢的方向,而且团队内技术氛围超棒),所以必须逼着自己以最短时间系统地读 Fli...

weixin_39539684的博客 344

Flink面试核心知识点拆解:从状态管理到实时数仓实战

在流式计算领域,Apache Flink凭借其真正的流处理引擎和强大的状态管理能力,成为实时计算场景中的首选技术。理解其核心原理,如Checkpoint与Barrier对齐机制,是掌握精确一次处理语义的关键,而这正是解决Kafka重平衡下数据一致性问题的基础。随着实时数仓架构的普及,Kafka与Flink的集成、JDBC连接器异常排查以及SASL安全认证配置,都成为工程师必须面对的工程实践挑战。从流处理基础架构到时间语义与窗口设计,再到连接器与SQL实践,系统地构建知识体系不仅有助于应对面试中的原理追问,更

weixin_34246529的博客 228

从6万字Flink面试题库看实时计算高频考点与复习策略

实时计算是大数据链路中响应最快的一环,Flink作为主流流处理引擎,其核心原理包括时间语义、水位线、状态管理与容错机制。理解这些基础概念,是排查数据乱序、状态膨胀、背压等工程问题的前提。在实际业务中,实时数仓、双流JOIN、CDC入湖入仓等场景都对开发者提出了更高要求。而面试正是检验这些技能的系统化场景。一份6万字、110个知识点的Flink面试题库,恰好覆盖了从核心架构、窗口机制到集群部署、参数调优的完整技能树。深入拆解这份题库背后的考察逻辑,梳理高频考点、常见送命题与高效复习方法,帮助读者建立从架构到调

weixin_29051149的博客 346

Flink 窗口核心机制剖析:Trigger、Evictor、Watermark 与状态管理

Trigger:窗口输出的“调度器”,决定窗口何时输出与清理,可灵活组合多种策略。Evictor:窗口元素的“守门员”,控制窗口内数据量,支持定制化剔除。Watermark:事件时间流进度的“心跳”,精准推进是实时与准确性的保障。状态管理:窗口聚合的根基,影响容错、扩展和性能。Trigger:窗口输出的“调度器”,决定窗口何时输出与清理,可灵活组合多种策略。Evictor:窗口元素的“守门员”,控制窗口内数据量,支持定制化剔除。Watermark。

IT技术学习与工作笔记分享 1310

CST微波工作室端口设置保姆级教程:从波导端口到离散端口,手把手教你搞定S参数仿真

本文提供CST微波工作室端口设置的详细教程,涵盖波导端口和离散端口的选择与配置,助力工程师精准完成S参数仿真。通过微带线滤波器案例,详解端口尺寸、阻抗设置及常见问题排查,提升仿真效率与准确性。

weixin_42570296的博客 362

Flink知识散点,springmvc源码分析图

前言 今天逛论坛,看到了一位35岁的老程序员发的博文,看完内容后我又活了,35岁挑战华为社招,竟然凭实力在半个月内经历4轮面试后成功拿到了offer,不得不佩服这位大哥,35岁还这么强我们这些后辈还怕啥! 当然重要的是这位程序员大佬最后总结的华为4轮面试所有的面试题和需要注意的事情。所以结合这个老哥面试问的问题和最近华为最新的面试题,重新整理了一下近期华为的面试题,满满干货,分享给大家。 3、Flink Runtime 层的整个架构主要是在 FLIP-6 中实现的,整体上采用了标准 master-slav

m0_69082654的博客 195

QuickFix Java 讲解(五)消息的收发与查看

本系列力求手把手教你怎样利用QuickFix Java 搭建自己的 FIX 协议收发平台,以及其中的注意事项。 所有源码的地址(免费): https://github.com/zongzhec/QuickFixPractise 这节我们讨论将Acceptor和Initiator启动以后,收到的消息极其查看方式。 6. 收发双方的通信 6.1. 启动后的通信和登录 为方便起见,这里我们使用统一的源码:https://github.com/zongzhec/QuickFixPractise ..

2247

哈工大2024春大数据挖掘期末考试回忆版试题

(3)现有一个用户数量远多于物品(还是物品远多于用户,忘了是哪个了)的情景,应该使用基于user-user的推荐系统还是item-itmen的推荐系统,说明原因。(1)L是n*n拉普拉斯矩阵,x是一个n*1列向量,证明对任意的x,(x)^TLx>=0(6分)(3)证明:p到o的K-可达距离,至少是o的K-distance(2分)(2)(1)的结果说明竞争率的上界是多少?(2)考Gain的意义,如何利用Gain选择划分点(2分)(2)说明如何利用划分后的数据进行验证(2分)(2)k可达距离是对称的吗?

ymd2002的博客 1892

Flink到底是怎么把你的程序抽象的?

我想我们大家都知道Flink是有状态的实时计算引擎,很多人不理解一个计算引擎应该怎么做呢,其实这就涉及到了Flink的核心,也就是它的应用程序抽象,我们都知道Flink会将我们编写的程序来进行转换成一个图

百看不如一RUN 4653

【计算机科学导论实验】加法图灵机模拟 题解

本文提供计算机科学导论课程“图灵机加法器”实验的个人思路,仅供参考。

Chavapa's blog 2579

【Flink 面试指南】Flink 详解(九):SQL 篇 Ⅱ(Flink SQL CEP)

在金融场景中,有时会出现银行卡盗刷现象,犯罪分子利用互联网等技术,在间隔 10 分钟或者更短时间内,使一张银行卡在不同的两个地方出现多次刷卡记录,这从常规操作来说,在间隔时间很多的情况下,用户是无法同时在两个城市进行刷卡交易的,所以出现这种问题,就需要后台做出触发报警机制。

Data · Model · Agent · Repeat 2456

VLA模型(具身智能入门十)

Vision Language Action(VLA)模型VLA模型是机器人领域的“感知-理解-执行”一体化框架,通过Transformer架构融合视觉、语言、动作模态,核心目标是让机器人在自然语言驱动下,自主完成复杂操控任务。其定义的核心在于“跨模态统一”与“具象化行动”——不仅能“看懂”“听懂”,更能“做出正确的动作”,是实现通用机器人智能的关键方向。提供通用序列建模骨架,让视觉、语言、动作能以“序列”形式统一处理;ViT将视觉输入转换为序列特征,提供“看”的能力;LLMs。

weixin_39699362的博客 9938

ESP32S3 Korvo2V3四通道AEC配置实战:基于AFE的回声消除优化

本文详细介绍了在ESP32S3 Korvo2V3平台上配置四通道AEC(回声消除)的实战经验。通过AFE算法优化和硬件连接调试,实现高质量回声消除,显著提升智能音箱和会议设备的语音交互体验。重点解析了四通道配置、数据格式对齐和参数调优等关键技术要点。

elastic6hunter的博客 938

【Flink 面试指南】Flink 详解(十):SQL 篇 Ⅲ(Flink SQL CDC)

在最新 CDC 调研报告中,Debezium 和 Canal 是目前最流行使用的 CDC 工具,这些 CDC 工具的核心原理是抽取数据库日志获取变更。在经过一系列调研后,目前 Debezium(支持全量、增量同步,同时支持 MySQL、PostgreSQL、Oracle 等数据库),使用较为广泛。

Data · Model · Agent · Repeat 1982

OpenClaw一人公司落地案例:本地商家营销智能体月赚3万的秘密

角色:本地商家营销专家## 核心目标为商家生成高转化率的抖音营销策略,最大化 ROI。## 输入数据- 商家名称: "XX奶茶店"- 过去 30 天视频播放量: 12,000- 客单价: 28 元- 竞品平均播放量: 15,000- 市场趋势: rising## 策略生成规则1. **内容类型**:根据客单价推荐- < 30 元:美食探店 + 优惠券- > 30 元:场景化故事 + 会员权益2. **发布时间**:根据历史数据- 18:00-20:00 高峰期。

专注于Python爬虫开发,分享爬虫技巧、项目实战与反爬经验,使用Scrapy、BeautifulSoup等工具,解决数据抓取难题。 543

3000字全面解析字节2-2数据开发岗位面经(建议收藏!)

刚到 2026 年年初,估计好多小伙伴也在找工作,这里给大家奉献一篇之前小伙伴面试字节的大数据架构师岗位面经

逆流而上Mr李 847

R语言Dunnett’s检验实战:寻找差异组之间的效果

总结起来,本文以R语言为工具,演示了如何使用Dunnett’s检验来找出单因素方差分析中不同组之间的效果差异。在本篇文章中,我们将通过R语言实战演示如何使用Dunnett’s检验来寻找不同组之间的效果差异。在分析结果时,通常我们会关注具有显著差异的组别,并基于Dunnett’s检验结果进行进一步的解释和讨论。首先,我们需要准备一组数据,假设我们有一个实验,涉及到四个不同的处理组和一个对照组。当我们发现方差分析结果显示存在显著差异时,我们可以进一步进行Dunnett’s检验来确定具体哪些组之间存在差异。

CoderExtra的博客 1104

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

【Flink 面试指南】Flink 详解(一):基础篇(架构、并行度、算子)

Flink 是一个以流为核心的高可用、高性能的分布式计算引擎。具备流批一体,高吞吐、低延迟,容错能力,大规模复杂计算等特点,在数据流上提供数据分发、通信等功能。

Data · Model · Agent · Repeat 6554

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

上一篇: 探知FlinkSQL背后默默支撑的“男人们“
下一篇: 一石激起千层浪:FlinkSQL Join维表和其他疑问的梳理
大数据左右手
博客等级 码龄9年 475粉丝 116原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值