Flink原始碼學習(6)StreamTask的初始化和執行

Heinrich♣發表於2024-05-03

Stream初始化

taskExecutor執行一個Task

當taskExecutor接受提交Task執行的請求,會呼叫:
CompletableFuture<Acknowledge> submitTask(
        TaskDeploymentDescriptor tdd, JobMasterId jobMasterId, @RpcTimeout Time timeout);
提交Task到TM
提交Job過來的JobManager和現在Active JobManager不是同一個的時候,就拒絕提交
初始化task的時候,內部初始化一個執行執行緒,就是task類啟動,把TDD變成Task
Task task =
        new Task(
                jobInformation,
                taskInformation,
                tdd.getExecutionAttemptId(),
                tdd.getAllocationId(),
                tdd.getProducedPartitions(),
                tdd.getInputGates(),
                memoryManager,
                sharedResources,
                taskExecutorServices.getIOManager(),
                taskExecutorServices.getShuffleEnvironment(),
                taskExecutorServices.getKvStateService(),
                taskExecutorServices.getBroadcastVariableManager(),
                taskExecutorServices.getTaskEventDispatcher(),
                externalResourceInfoProvider,
                taskStateManager,
                taskManagerActions,
                inputSplitProvider,
                checkpointResponder,
                taskOperatorEventGateway,
                aggregateManager,
                classLoaderHandle,
                fileCache,
                taskManagerConfiguration,
                taskMetricGroup,
                partitionStateChecker,
                getRpcService().getScheduledExecutor(),
                channelStateExecutorFactoryManager.getOrCreateExecutorFactory(jobId));
  1. task的構造方法

    1. 封裝一個TaskInfo,把task在執行過程中必備的描述資訊放入
      this.taskInfo =
              new TaskInfo(
                      taskInformation.getTaskName(),
                      taskInformation.getMaxNumberOfSubtasks(),
                      executionAttemptID.getSubtaskIndex(),
                      taskInformation.getNumberOfSubtasks(),
                      executionAttemptID.getAttemptNumber(),
                      String.valueOf(slotAllocationId));

    2. 建立shuffle context
      final ShuffleIOOwnerContext taskShuffleContext =
              shuffleEnvironment.createShuffleIOOwnerContext(
                      taskNameWithSubtaskAndId, executionId, metrics.getIOMetricGroup());

    3. 初始化ResultPartition和ResultSubPartition
  1. 一個Task的執行有輸入和輸出,關於輸出的抽象ResultPartition和ResultSubPartition
  2. 具體實現是resultPartition
  3. 批處理建立一個ReleaseOnConsumptionResultPartition,流處理建立ResultPartition
  4. subPartition的數量等於下游task的個數
  1. 輸入的抽象是InputGate和InputChannel,也會init
    1.   singleInputGate建立一個給inputGate

        建立inputChannel陣列,有兩種情況,createUnknownInputChannel也可能是createKnownInputChannel,正常情況是known的。當前是本地獲取資料的話localRecoveredInputChannel,正常是remote

  2. 初始化一個thread,task的構造方法
  3. sourceStreamTask和streamTask初始化
    1. 建立recordWriter
    2. 處理輸入streamTask.processInput=new mailboxProcess
    3. 建立狀態後端stateBackend
    4. 透過stateBackend建立checkpointStorage
    5. 獲取分流器
      1. 用於調整並行度,如果上下游streamNode的並行度一致就用forwardPartition分發策略
      2. 如果不一致就使用RebalancePartition實現

    

提交job到standalone叢集執行的時候,在client構建streamGraph頂點式streamNode,邊是streamEdge的時候,根據生成的transformation為streamGraph生成stramNode,生成Node的hi後判斷如果該operator是streamSource的話,就會指定該streamTask的invokableClass為sourceStreamTask
task.Run
流式引擎,上游task執行完一條資料計算,就會傳送結果給下游task,規則由streamPartitioner來指定
一條資料在一個task執行完畢之後,就要傳送給下游另外一個task,這個過程,網路資料傳輸過程,由netty支援的,具體是由inputChannel實現的
  1. 狀態從created、改為deploying
  2. 拉起resultPartitionWriter和inputGate
    1. 構造task的時候,就已經初始化過了
    2. 註冊result Partition Writer
    3. 如果註冊過了就報錯
    4. inputChannel分配buffer
  3. 包裝task的各種元件 environment
  4. 透過反射例項化streamTask
    1. sourceStreamTask
      1. 是flink job最開始的task,對接source,有一個專門的執行緒接受資料
      2. source用於產生data的一個執行緒
    2. oneInputStreamTask
  5. 狀態從deploying改為running
  6. 啟動streamTask
  7. streamTask正常結束處理buffer資料
  8. 狀態從running改為finished
state管理原始碼剖析
flink具有有狀態計算的特點
state是flink job的task執行過程中產生的中間資料,輔助task執行計算,同時儲存中間狀態幫助flink job的重啟恢復。儲存狀態非常重要
state要配合checkpoint機制保證flink失敗之後正確的回覆錯誤
flink自己週期性的快照叫checkpoint,手動儲存叫savepoint
flink狀態分類
keyedState具體的key繫結,只能在keyedStream上的函式和運算元中使用
OperatorState和operator的一個特定的並行例項繫結,例如kafka connector
Keyed state也可以看作是operator state的一種分割槽partition形式,每一個key都關聯一個狀態分割槽state-partition
無論是operator state還是keyed state都有兩種形式:Managed state和raw state。
所有跟state有關的都由stateBackend完成,核心有abstractStateBackend,三種儲存型別
FileSystem將state儲存在taskmanager的記憶體中,checkpoint儲存在檔案系統記憶體中,適合儲存量大,檔案系統中需要序列化反序列化的開銷速度慢點,但是比memory好
Memory 儲存在記憶體中,很快,適合小state任務,不能上生產。儲存在記憶體中
RocksDB,基於記憶體的資料庫系統,把狀態儲存在reocksDB,ckeckpoint存在檔案系統,比memory快,GC少,支援非同步snapshot持久化,用於儲存state量大的,window視窗很長的一些job合適
  1. StreamTask執行

  1. beforeInvoke
    1. operatorChain的初始化,recoveredChannelState初始化
    2. 當第一個operator執行完畢的時候就有collector收集計算結果再去呼叫第二個operator執行processElement,透過output收集處理之後的結果資料
      1. userFunction.run:function->transformation->streamOperator,headOperator的run方法
      2. socket連線讀資料
      3. channelSelector確定目標channel決定record被分發到哪一個分割槽
  2. runMailboxLoop
不停的處理mail
  1. afterInvoke
完成task結束之前的操作
  1. cleanUpInvoke

相關文章