Apache Flink 源码解析(二)(Legacy mode已废弃)系统架构, 启动与注册

概述

这篇文章侧重于分析JobManager和TaskManager的启动过程以及注册,还有Flink的implementation中所用到的设计模式。本文从本地与standalone模式进行解析。

Akka 简介

  • 因为组件之间的信息传递是通过Akka工具包,所以在这儿我做一个简单的解释
  • 首先参考Akka官方文档给出的一个抽象的图


    image
  • 如图就是对ActorSystem的一个高度抽象,所有的Actor成树状,user actor的子孙就是用户创建的Actor,system下面是Akka的监管与支持的Actor,往往不需要用户过多参与
  • Actor之间如果持有对方的ActorRef则可以向对方发送消息
  • 父Actor负责监管子Actor抛出的异常能被父Actor处理,则父Actor可以重启或者废弃它,如果不能处理,会继续向上抛异常。一个Actor如果推出,那么它的所有子Actor都会退出。
  • 它们共同组成了一个ActorSystem

Flink的架构

  • 首先上一张从Flink官方文档拿来的架构图


    process-model.png
  • 这次我会从JobManager和TaskManager着手来解析Flink的启动过程

  • Flink中JobManager与TaskManager,JobManager与Client的交互是基于Akka工具包的,是通过消息驱动,这样就把中心放到了消息的接收,发送与处理,而且由于对每个Akka中的Actor来说,消息是同步的,排在一个队列中,极大地简化了多线程程序设计的复杂度,关于Akka的一些学习资料我会放在文章最后

  • 下面的启动过程我会分为入口,ActorSystem的创建,JobManager的启动,TaskManager的启动和注册这几块来讲

    入口

    • 如果看过该系列上一篇文章,应该知道怎么找到在Local和Standalone模式下程序的入口

    • 这里我直接给出来,就是org.apache.flink.runtime.jobmanager 中的main方法

    • main方法主要可以分为,一是启动环境的准备


      JobManager Startup.png
    • 二是处理输入的命令行参数,并准备好包含配置信息的Configuration对象,以及host地址与端口号,还有运行模式


      configuration prepare.png
    • 三是新建一个Callable对象,在另一个线程上运行runJobManager方法


      mainRunJobManager.png
    • runJobManager有 个重载方法,第一个方法中绑定了端口号并建立了socket链接,并调用第二个重载方法


      bindport.png
    • 第二个重载方法根据本机硬件情况建立futureExecutor(对Future类不了解的话可以简单理解成Java中带返回值的Runnable,细节不做讨论), ioExecutor,并调用startActorSystemAndJobManagerActors方法建立ActorSystem并等待结束


      startActorSystem.png

    ActorSystem创建

    • 在这里Flink做了一个Akka的工具类来简化逻辑,本质上就是从Configuration配置文件类中提取Akka启动所需配置信息,并根据配置建立ActorSystem,ActorSystem可以是本机的,也可以是跨多台机器的,具体逻辑都在AkkaUtils中,有兴趣可以研究
      startJobManagerSystem.png
    • 建立JobManager的可视化WebMonitor,这里不做介绍

    • 重点出现,在这里通过ActorSystem建立了JobManager的Actor,并建立了一个JobManager process reaper来做简单的失败检测


      createJobManager.png
    • (*)如果实在Local模式下的话,则启动一个内嵌的TaskManager,如果不是,则需要在另一个JVM中启动TaskManager,通过taskmanager.sh脚本来完成


      localTaskManager.png
    • 针对每一个TaskManager Actor再启动一个WebMonitor可视化界面

    JobManager

    • 对于JobManager与TaskManager的启动与关闭,会有三个环节,preStart,handleMessage与postStop
    • 对于每一个Actor,必须Override抽象方法handleMessage,也就是Actor针对收到的message做的业务逻辑,可以选择Override preStart和postStop方法,做一些启动前准备与结束时的清理
    • (关于Scala的多重继承,以及菱形继承的问题我这边不做过多讨论,网上有一些帖子,也可以通过阅读Scala In Depth来获得更深入了解,这对Java程序员来说是一个新的概念)
    • 首先是启动前准备preStart
      • 启动leaderElectionService,将JobManager本身作为参数传入


        startLeaderElectionService.png

        这边值得一提的是设计模式,策略模式与观察者模式,因为leaderElectionService是一个Java的接口,在生产环境中有非高可用(单点失败)的Implementation与基于Zookeeper的高可用模式,可以再运行时更改该接口的行为。JobManager还实现了LeaderContender接口,实现了多个CallBack方法,当leaderElectionService被修改时,会通知JobManager来调整,典型的观察者模式,适用于在高可用模式下作为Leader的JobManager被更改的情况。

      • 接着是启动SubmittedJobGraph服务,失败恢复服务与Metrics,这些会在以后讲到
      • 在这儿调用了leaderElectionService,对高可用模式的解析可能会在以后补上,现在侧重于Standalone模式下的解析,在StandaloneLeaderElectionService中,因为只有一个JobManager,所以直接在start时调用LeaderContentder中的callback方法,也就是JobManager的grantLeadership方法


        grantLeadership Method.png
      • 在该方法中向JobManager Actor本身发送了一条消息,从而在handleMessage中进行接收处理(!在Scala的Akka包中是发送消息的方法)
    • handleMessage(启动相关)
      • 根据消息的类型进行匹配,当接收到GrantLeadership的Message后,会匹配到如下代码


        handleGrantLeadershipMessage.png

        首先确认身份,再判断是否为高可用模式,如果是高可用模式还需要发送恢复任务的消息,如果不是,JobManager的启动已完成

    TaskManager

    • TaskManager Actor创建
      • 如果为本地模式,则直接调用startTaskManagerComponentAndActor方法,如果是用脚本启动,则会进入TaskManager的main函数
      • 对于standalone模式
        • 在main函数中解析完命令行参数并生成配置文件对象后,会生成resourceID作为身份


          resourceIDGenerate.png
        • 接下来会新建一个Callable对象并调用selectNetworkInterfaceAndRunTaskManager方法


          selectNetworkAndRunTaskManager.png
        • 在selectNetworkInterfaceAndRunTaskManager方法中,先绑定地址与端口号,建立远程ActorSystem,然后调用runTaskManager方法


          runTaskManager1.png
        • 在runTaskManager方法中通过调用startTaskManagerComponentsAndActor并传入远程ActorSystem,至此与local模式的启动一致,区别在于ActorSystem是与JobManager一致还是远程另外一个ActorSystem,但对于开发者来说对Actor之间的消息传递方法并没有任何区别


          startTaskManagerComponentsAndActorRemote.png
      • 在startTaskManagerComponentsAndActor创建ioManager,network,leaderRetrievalService等创建TaskManager所需要的参数,通过actorSystem来创建TaskManager Actor


        createTaskManager.png
    • prestart
      • 首先启动leaderRetrievalService,和LeaderElection一样使用了策略模式来处理是否高可用两种情况,观察者模式来接收对象变化并调用callback方法


        startLeaderRetrievalService.png
      • 这边同样关注Standalone版本的Implementation,在start中调用TaskManager的notifyLeaderAddress回调方法,并将jobManager地址作为参数传入


        ldeaderRetrievalServiceStart.png
      • 在TaskManager实现的notifyLeaderAddress方法中发送JobManagerLeaderAddress消息给自己


        notifyLeaderAddress.png
    • handleMessage(启动相关)
      • TaskManager Actor一旦接收到该Message要不就是刚启动,要不就是JobManager的Leader发生了改变,调用handleJobManagerLeaderAddress函数
        handleGrantLeadershipMessage.png
      • 在handleJobManagerLeaderAddress函数中,先断开连接,然后出发TaskManager的注册操作


        handleJobManagerLeaderAddressMessage.png

    TaskManager的注册

    • 在TaskManager的注册中,设计了与JobManager的消息交互,所以单独分开来讲
    • TaskManager中的发送注册请求
      • 在handleJobManagerLeaderAddress中触发了triggerTaskManagerRegistration注册函数
      • 在该函数中,提取超时信息设置,以及当前尝试的ID,清空已经在调度器中应该被废弃的注册消息,并向自身发送尝试次数为第一次的TriggerTaskManagerRegistration消息


        triggerTaskManagerRegistration.png
      • 因为TriggerTaskManagerRegistration是在TaskManager Actor接收到RegistrationMessage的子类,所以在接收到该消息时,根据RegistrationMessage来匹配,并调用handleRegistrationMessage方法


        handleRegistrationMessage.png
      • 匹配到TriggerTaskManagerRegistration消息后,先判断该消息有没有失效,如果没有,则有三种情况


        handleTriggerTaskManagerRegistration.png
      • 如果已连接成功,写入日志
      • 如果超时,写入日志并推出
      • 除此之外,进行下一次尝试,向JobManager Actor发送RegisterTaskManager消息,并在调度其中注册下一次TriggerTaskManagerRegistration的消息的发送,知道出现第一种情况注册成功或第二种情况注册失败为止
    • JobManager接收到注册请求消息


      handleRegisterTaskManager.png
      • 根据消息找到相应的TaskManager地址
      • 一旦JobManager接收到RegisterTaskManager消息,先想ResourceManager注册(这边不做介绍,这边的?是Akka里面发送ask消息并期望得到一个返回值),如果Resource注册失败则向发送ReconnectResourceManager消息进行重试
      • 如果该TaskManager已经注册在instanceManager中,则发送AlreadyRegistered消息给相应的TaskManager
      • 如果还未注册,则向instanceManager注册该TaskManager,并发送AcknowledgeRegistration给相应的TaskManager
      • 出现异常则拒绝注册,发送RefuseRegistration消息
    • TaskManager接到返回消息
      • AcknowledgeRegistration注册成功,如果isConnected为true则是已连接,判断该消息是否由当前链接的JobManager发送并写入日志,如果未连接,调用associateWithJobManager进行接收消息钱的准备工作(后续会深入解析)


        ackRegistration.png
      • AlreadyRegistered重复注册,写入日志,逻辑与 AcknowledgeRegistration消息的处理相同
      • RefuseRegistration注册失败,如果JobManager地址存在,则发送新的TriggerTaskManagerRegistration, 重复到TaskManager注册部分的福州,如果没有地址,则验证如果发送消息的JobManager是否是当前已连接的JobManager写入日志,对结果没有影响

总结

至此,JobManager和TaskManager的启动过程以及TaskManager的注册过程解析已经完成。解析中没有办法做到面面俱到,把自己觉得重要的点挑了出来,主要是能再时间上形成一个线,方便理解。
下面还有一个根据时间线来做的思维导图,侧重于Local模式下的启动,虚线代表调用或者是消息。


Flink start up.jpg

附录
Akka学习资料:

  1. Akka 官方文档 link
  2. Akka 手册翻译 link 翻译有一些晦涩
  3. Akka 系列博客 link

你可能感兴趣的:(Apache Flink 源码解析(二)(Legacy mode已废弃)系统架构, 启动与注册)