flink源碼閱讀---單作業單集羣作業提交流程

flink on yarn 模式支持兩種部署方式:

1. 多作業但集羣

2. 單作業但集羣

本文主要介紹單作業單集羣下作業提交流程:

核心組件:

Job CLI: 即flink run,非 detatched 模式下的客戶端進程,用以獲取 yarn Application Master 的運行狀態並將日誌輸出掉終端

Job Manager[JM]: 負責作業的運行計劃ExecutionGraph的生成,物理計劃生成和作業調度

TaskManager[TM]:負責被分發 task 的執行、心跳/狀態上報、資源管理

啓動方式:

啓動Flink Yarn Session有2種模式:分離模式、客戶端模式

通過-d指定分離模式,即客戶端在啓動Flink Yarn Session後,就不再屬於Yarn Cluster的一部分。如果想要停止Flink Yarn Application,需要通過yarn application -kill 命令來停止。

作業提交整體流程:

提交流程詳細分析:

啓動命令:

./bin/flink run -m yarn-cluster -yn 2 -j flink-demo-1.0.0-with-dependencies.jar —ytm 1024 -yst 4 -yjm 1024 —yarnname

flink 在收到這樣一條命令後會首先通過 Cli 獲取 flink 的配置,並解析命令行參數。

程序入口

CliFrontend.java 是 flink 提交作業的入口

解析參數,並路由到 run方法

解析參數,構建program, 獲取當前集羣模式入口即:FlinkYarnSessionCli 和 DefaultCli

其中FlinkYarnSessionCli是flink yarn session 模式,DefaultCli爲standalone模式

flink集羣構建:

flink 通過 兩個不同的 CustomCommandLine 來實現不同集羣模式的解析,分別是 FlinkYarnSessionCli和 DefaultCLI 解析命令行參數:

final CustomCommandLine<?> customCommandLine = getActiveCustomCommandLine(commandLine);

那麼什麼時候解析成 Yarn Cluster 什麼時候解析成 Standalone 呢?由於FlinkYarnSessionCli被優先添加到customCommandLine,所以會先觸發下面這段邏輯:

public boolean isActive(CommandLine commandLine) {

   String jobManagerOption = commandLine.getOptionValue(addressOption.getOpt(), null);

   boolean yarnJobManager = ID.equals(jobManagerOption);

   boolean yarnAppId = commandLine.hasOption(applicationId.getOpt());

   return yarnJobManager || yarnAppId || (isYarnPropertiesFileMode(commandLine) && yarnApplicationIdFromYarnProperties != null);

}

 

從上面可以看出如果用戶傳入了 -m參數或者application id或者配置了yarn properties 文件,則啓動yarn cluster模式,否則是Standalone模式的集羣

最後獲取YarnClusterDescriptor,YarnClusterDescriptor來描述yarn集羣的部署配置,具體對應的配置文件爲flink-conf.yaml

flink集羣部署:

flink通過YarnClusterDescriptor來描述yarn集羣的部署配置,具體對應的配置文件爲flink-conf.yaml。

獲取YarnClusterDescriptor

初始化JobGraph:

 

部署集羣:

大致過程:

  • check yarn 集羣隊列資源是否滿足請求

  • 設置 AM Context、啓動命令、submission context

  • 通過 yarn client submit am context

  • 將yarn client 及相關配置封裝成 YarnClusterClient 返回

真正在 AM 中運行的主類是 YarnApplicationMasterRunner,它的 run方法做了如下工作:

  • 啓動JobManager ActorSystem

  • 啓動 flink ui

  • 啓動YarnFlinkResourceManager來負責與yarn的ResourceManager交互,管理yarn資源

  • 啓動 actor System supervise 進程

到這裏 JobManager 已經啓動起來

這樣一個 flink 集羣便構建出來了。下面附圖解釋下這個流程:

  1. flink cli 解析本地環境配置,啓動 ApplicationMaster

  2. 在 ApplicationMaster 中啓動 JobManager

  3. 在 ApplicationMaster 中啓動YarnFlinkResourceManager

  4. YarnFlinkResourceManager給JobManager發送註冊信息

  5. YarnFlinkResourceManager註冊成功後,JobManager給YarnFlinkResourceManager發送註冊成功信息

  6. YarnFlinkResourceManage知道自己註冊成功後像ResourceManager申請和TaskManager數量對等的 container

  7. 在container中啓動TaskManager

  8. TaskManager將自己註冊到JobManager中

flink作業提交執行:

作業的執行其實只是調用了用戶jar包的主函數,真正的觸發生成過程由用戶代碼的執行來完成。

獲取Environment

val env = StreamExecutionEnvironment.getExecutionEnvironment,這段代碼會獲取客戶端的環境配置,它首先會轉到這樣一段邏輯:

//StreamExecutionEnvironment 1256

public static StreamExecutionEnvironment getExecutionEnvironment() {

   if (contextEnvironmentFactory != null) {

      return contextEnvironmentFactory.createExecutionEnvironment();

   }

   // because the streaming project depends on "flink-clients" (and not the other way around)

   // we currently need to intercept the data set environment and create a dependent stream env.

   // this should be fixed once we rework the project dependencies

  

   ExecutionEnvironment env = ExecutionEnvironment.getExecutionEnvironment();

ExecutionEnvironment.getExecutionEnvironment();獲取環境的邏輯如下:

//ExecutionEnvironment line1137

public static ExecutionEnvironment getExecutionEnvironment() {

   return contextEnvironmentFactory == null ?

         createLocalEnvironment() : contextEnvironmentFactory.createExecutionEnvironment();

}

這裏的contextEnvironmentFactory是一個靜態成員,早在ContextEnvironment.setAsContext(factory)已經觸發過初始化了,其中包含了如下的環境信息:

//ContextEnvironmentFactory line51

public ContextEnvironmentFactory(ClusterClient client, List<URL> jarFilesToAttach,

      List<URL> classpathsToAttach, ClassLoader userCodeClassLoader, int defaultParallelism,

      boolean isDetached, String savepointPath)

{

   this.client = client;

   this.jarFilesToAttach = jarFilesToAttach;

   this.classpathsToAttach = classpathsToAttach;

   this.userCodeClassLoader = userCodeClassLoader;

   this.defaultParallelism = defaultParallelism;

   this.isDetached = isDetached;

   this.savepointPath = savepointPath;

}

其中的 client 就是上面生成的 YarnClusterClient,

用戶在執行val env = StreamExecutionEnvironment.getExecutionEnvironment這樣一段邏輯後會得到一個StreamContextEnvironment,其中封裝了 streaming 的一些執行配置 【buffer time out等】,另外保存了上面提到的 ContextEnvironment 的引用。

到這裏關於 streaming 需要的執行環境信息已經設置完成。

StreamGraph 的生成:

接下來用戶代碼執行到DataStream<String> stream = env.addSource(consumer);這段邏輯實際會生成一個DataStream抽象,DataStream是flink關於streaming抽象的最核心抽象,後續所有的算子轉換都會在DataStream上來完成,上面的addSource操作會觸發下面這段邏輯:

public <OUT> DataStreamSource<OUT> addSource(SourceFunction<OUT> function, String sourceName, TypeInformation<OUT> typeInfo) {

   if (typeInfo == null) {

      if (function instanceof ResultTypeQueryable) {

         typeInfo = ((ResultTypeQueryable<OUT>) function).getProducedType();

      } else {

         try {

            typeInfo = TypeExtractor.createTypeInfo(

                  SourceFunction.class,

                  function.getClass(), 0, null, null);

         } catch (final InvalidTypesException e) {

            typeInfo = (TypeInformation<OUT>) new MissingTypeInfo(sourceName, e);

         }

      }

   }

   boolean isParallel = function instanceof ParallelSourceFunction;

   clean(function);

   StreamSource<OUT, ?> sourceOperator;

   if (function instanceof StoppableFunction) {

      sourceOperator = new StoppableStreamSource<>(cast2StoppableSourceFunction(function));

   } else {

      sourceOperator = new StreamSource<>(function);

   }

   return new DataStreamSource<>(this, typeInfo, sourceOperator, isParallel, sourceName);

}

簡要總結下上面的邏輯:

  • 獲取數據源 source 的 output 信息 TypeInformation

  • 生成 StreamSource sourceOperator

  • 生成 DataStreamSource【封裝了 sourceOperator】,並返回

  • 將 StreamTransformation 添加到算子列表 transformations 中【只有 轉換 transform 操作纔會添加算子,其它都只是暫時做了 transformation 的疊加封裝】

  • 後續會在 DataStream 上做操作

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章