Flink源码系列——JobManager处理SubmitJob的过程

本文详细介绍了Flink JobManager在接收到SubmitJob消息后,如何处理JobGraph,构建ExecutionGraph以及对其进行调度执行的过程。ExecutionGraph是JobGraph的并行模式,通过ExecutionGraphBuilder构建,然后按照LAZY_FROM_SOURCES或EAGER模式进行调度,包括槽位分配和部署等关键步骤。
摘要由CSDN通过智能技术生成

《Flink源码系列——获取JobGraph的过程》,在获取到JobGraph后,客户端会封装一个SubmitJob消息,并将其提交给JobManager,本文就接着分析,JobManager在收到SubmitJob消息后,对其处理逻辑。JobManager是一个Actor,其对接受到的各种消息的处理入口是handleMessage这个方法,其中对SubmitJob的处理入口如下:

override def handleMessage: Receive = {
    …

    case SubmitJob(jobGraph, listeningBehaviour) =>
        val client = sender()
        val jobInfo = new JobInfo(client, listeningBehaviour, System.currentTimeMillis(), jobGraph.getSessionTimeout)

        submitJob(jobGraph, jobInfo)

    …
}

这里构造了一个JobInfo实例,其是用来保存job的相关信息的,如提交job的客户端、客户端监听模式、任务提交的开始时间、会话超时时间、以及结束时间、耗时等信息。
其中监听模式有三种,三种模型下关心的消息内容依次增加,解释如下:

a、DETACHED —— 只关心job提交的确认消息
b、EXECUTION_RESULT —— 还关心job的执行结果
c、EXECUTION_RESULT_AND_STATE_CHANGES —— 还关心job的状态变化

然后就进入了真正的处理逻辑subminJob()方法中了,这个方法的代码稍微有点长,这里就分段进行分析,另外submitJob这个方法除了上述的jobGraph和jobInfo两个入参外,还有一个isRecovery的布尔变量,默认值是false,用来标识当前处理的是否是一个job的恢复操作。这个逻辑根据jobGraph是否为null分为两个大的分支,先看下jobGraph为null的情况,处理逻辑就是构造一个job提交异常的消息,然后通知客户端,告诉客户端jobGraph不能为null。

jobInfo.notifyClients(
  decorateMessage(JobResultFailure(
    new SerializedThrowable(
      new JobSubmissionException(null, "JobGraph must not be null.")))))

重点还是分析jobGraph不为null的情况下的处理逻辑,这部分的逻辑也可以分为两大部分。

a、根据jobGraph构建ExecutionGraph
b、对构建好的ExecutionGraph进行调度执行

在构建ExecutionGraph这部分,会进行一些初始化的工作,如果在这过程中,发生异常,会将初始化过程做的操作进行回滚操作。


1、构建ExecutionGraph

1.1、总览

在开始ExecutionGraph的构建之前,会先获取构建所需的参数,如下:

/** 将job所需jar相关信息注册到library管理器中,如果注册失败,则抛出异常 */
try {
  libraryCacheManager.registerJob(
    jobGraph.getJobID, jobGraph.getUserJarBlobKeys, jobGraph.getClasspaths)
}
catch {
  case t: Throwable =>
    throw new JobSubmissionException(jobId,
      "Cannot set up the user code libraries: " + t.getMessage, t)
}

/** 获取用户类加载器,如果获取的类加载器为null,则抛出异常 */
val userCodeLoader = libraryCacheManager.getClassLoader(jobGraph.getJobID)
if (userCodeLoader == null) {
  throw new JobSubmissionException(jobId,
    "The user code class loader could not be initialized.")
}

/** 判断{
   @code JobGraph}中的{
   @code StreamNode}的个数, 如果为0, 则说明是个空任务,抛出异常 */
if (jobGraph.getNumberOfVertices == 0) {
  throw new JobSubmissionException(jobId, "The given job is empty")
}

/** 优先采用JobGraph配置的重启策略,如果没有配置,则采用JobManager中配置的重启策略 */
val restartStrategy =
  Option(jobGraph.getSerializedExecutionConfig()
    .deserializeValue(userCodeLoader)
    .getRestartStrategy())
    .map(RestartStrategyFactory.createRestartStrategy)
    .filter(p => p != null) match {
    case Some(strategy) => strategy
    case None => restartStrategyFactory.createRestartStrategy()
  }

log.info(s"Using restart strategy $restartStrategy for $jobId.")

val jobMetrics = jobManagerMetricGroup.addJob(jobGraph)

/** 获取注册在调度器上的所有TaskManager实例的总的slot数量 */
val numSlots = scheduler.getTotalNumberOfSlots()

/** 针对jobID,看是否已经存在 ExecutionGraph,如果有,则直接获取已有的,并将registerNewGraph标识为false */
val registerNewGraph = currentJobs.get(jobGraph.getJobID) match {
  case Some((graph, currentJobInfo)) =>
    executionGraph = graph
    currentJobInfo.setLastActive()
    false
  case None =>
    true
}

上面这段逻辑主要做一些准备工作,如jar包注册,类加载器,重启策略等,这些准备好之后,就可以开始ExecutionGraph的构建,调用如下:

/** 通过{
   @link JobGraph}构建出{
   @link ExecutionGraph} */
executionGraph = ExecutionGraphBuilder.buildGraph(
  executionGraph,
  jobGraph,
  flinkConfiguration,
  futureExecutor,
  ioExecutor,
  scheduler,
  userCodeLoader,
  checkpointRecoveryFactory,
  Time.of(timeout.length, timeout.unit),
  restartStrategy,
  jobMetrics,
  numSlots,
  blobServer,
  log.logger)

/** 如果还没有注册过, 则进行注册 */
if (registerNewGraph) {
  currentJobs.put(jobGraph.getJobID, (executionGraph, jobInfo))
}

/** 注册job状态变化监听器 */
executionGraph.registerJobStatusListener(
  new StatusListenerMessenger(self, leaderSessionID.orNull))

jobInfo.clients foreach {
  /** 如果客户端关心执行结果和状态变化,则为客户端在executiongraph中注册相应的监听器 */
  case (client, ListeningBehaviour.EXECUTION_RESULT_AND_STATE_CHANGES) =>
    val listener  = new StatusListenerMessenger(client, leaderSessionID.orNull)
    executionGraph.registerExecutionListener(listener)
    executionGraph.registerJobStatusListener(listener)
  case _ => // 如果不关心,则什么都不做
}

在ExecutionGraph构建好只有,就会设置相应的监听器,用来监听其后续的调度执行情况。
另外这段代码的执行会被整个的进行了try…catch,看下catch中的逻辑。

/** 如果异常, 则进行回收操作 */
case t: Throwable =>
  log.error(s"Failed to submit job $jobId ($jobName)", t)
  /** 进行jar包的注册回滚 */
  libraryCacheManager.unregisterJob(jobId)
  blobServer.cleanupJob(jobId)
  /** 移除上面注册的graph */
  currentJobs.remove(jobId)
  /** 如果executionGraph不为null,还需要执行failGlobal操作 */
  if (executionGraph != null) {
    executionGraph.failGlobal(t)
  }
  /** 构建JobExecutionException移除 */
  val rt: Throwable = if (t.isInstanceOf[JobExecutionException]) {
    t
  } else {
    new JobExecutionException(jobId, s"Failed to submit job $jobId ($jobName)", t)
  }
  /** 通知
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值