Flink的架構

Panda_XiaoXi發表於2019-01-02

Apache Flink是一個分散式框架處理引擎,用於對無界和有界資料流進行有狀態計算。Flink執行在所有常見的叢集環境中執行,高效率的執行計算。

整體的架構

處理的資料

無界資料流:有一個開始但沒有定義的結束。它們不會在生成時終止並提供資料。必須持續處理無界流,即必須在攝取事件後立即處理事件。無法等待所有輸入資料到達,因為輸入是無界的,並且在任何時間點都不會完成。處理無界資料通常要求以特定順序(例如事件發生的順序)攝取事件,以便能夠推斷結果完整性。(即流資料)

有界資料流:具有定義的開始和結束。可以在執行任何計算之前通過攝取所有資料來處理有界流。處理有界流不需要有序攝取,因為可以始終對有界資料集進行排序。(即批處理)

如下圖:

批處理和流處理

和其他大資料平臺的部署及相容

Apache Flink是一個分散式系統,需要計算資源才能執行應用程式。Flink與所有常見的叢集資源管理器(如Hadoop YARN,Apache Mesos和Kubernetes)整合,但也可以設定為作為獨立叢集執行。

主要處理的資料狀態

Flink旨在以任何規模執行有狀態流應用程式。應用程式可以並行化為數千個在叢集中分佈和同時執行的任務。(有狀態和無狀態的區別-> 有狀態物件(Stateful Bean),就是有例項變數的物件,可以儲存資料,是非執行緒安全的。 無狀態物件(Stateless Bean),就是沒有例項變數的物件,不能儲存資料,是不變類,是執行緒安全的。)

對資料狀態的一些優化

有狀態Flink應用程式針對本地狀態訪問進行了優化。任務狀態始終保留在記憶體中,或者,如果狀態大小超過可用記憶體,則儲存在訪問高效的磁碟上資料結構中。因此,任務通過訪問本地(通常是記憶體中)狀態來執行所有計算,從而產生非常低的處理延遲。Flink通過定期和非同步檢查本地狀態到持久儲存來保證在出現故障時的一次狀態一致性。 如下圖:

任務狀態優化

支援的API

Flink提供三種的API:

支援三層API

  • SQL和Table API Flink有兩個關係API,Table API和SQL。這兩個API都是用於批處理和流處理的統一API,即,在無界的實時流或有界的記錄流上以相同的語義執行查詢,併產生相同的結果。Table API和SQL利用Apache Calcite進行解析,驗證和查詢優化。它們可以與DataStream和DataSet API無縫整合,並支援使用者定義的標量,聚合和表值函式。 以下SQL用於對點選流進行會話並計算每個會話的點選次數的SQL查詢:

      SELECT userId, COUNT(*)
      FROM clicks
      GROUP BY SESSION(clicktime, INTERVAL '30' MINUTE), userId
    複製程式碼
  • ProcessFunctions是Flink提供的最具表現力的功能介面。Flink提供ProcessFunctions來處理來自視窗中分組的一個或兩個輸入流或事件的單個事件。ProcessFunctions提供對時間和狀態的細粒度控制。ProcessFunction可以任意修改其狀態並註冊將在未來觸發回撥函式的定時器。因此,ProcessFunctions可以實現許多有狀態事件驅動應用程式所需的複雜的每事件業務邏輯。以下示例顯示了KeyedProcessFunction對a KeyedStream和match START以及END事件進行操作的示例。當一個START被接收的事件,則該函式在記住其狀態時間戳和計時在四個小時的計時器。如果END在計時器觸發之前收到事件,則該函式計算事件END和START事件之間的持續時間,清除狀態並返回值。否則,計時器只會觸發並清除狀態。

      /**
       * 匹配流入的START和END事件,並計算兩個元素的時間的差;
       *  第一個String欄位是鍵屬性,第二個String屬性標記START和END事件。
       */
      public static class StartEndDuration
          extends KeyedProcessFunction<String, Tuple2<String, String>, Tuple2<String, Long>> {
      
        private ValueState<Long> startTime;
      
        @Override
        public void open(Configuration conf) {
          // 獲取狀態處理
          startTime = getRuntimeContext()
            .getState(new ValueStateDescriptor<Long>("startTime", Long.class));
        }
      
        @Override
        public void processElement(
            Tuple2<String, String> in,
            Context ctx,
            Collector<Tuple2<String, Long>> out) throws Exception {
      
          switch (in.f1) {
            case "START":
              // 如果接受到一個開始事件,則設定開始時間
              startTime.update(ctx.timestamp());
              // 註冊一個計時器,從開始時間開始的四個小時內
              ctx.timerService()
                .registerEventTimeTimer(ctx.timestamp() + 4 * 60 * 60 * 1000);
              break;
            case "END":
              // 發出開始和結束事件之間的持續時間
              Long sTime = startTime.value();
              if (sTime != null) {
                out.collect(Tuple2.of(in.f0, ctx.timestamp() - sTime));
                // 清除狀態
                startTime.clear();
              }
            default:
              // do nothing
          }
        }
      
        /** 計時器觸發時呼叫 */
        @Override
        public void onTimer(
            long timestamp,
            OnTimerContext ctx,
            Collector<Tuple2<String, Long>> out) {
      
          // 超時時,清除狀態
          startTime.clear();
        }
      }
    複製程式碼
  • DataStream API所述的資料流中的API通過查詢外部資料儲存提供了許多常見的流處理操作。資料流API可用於Java和Scala和基於功能,如map(),reduce()和aggregate()。可以通過擴充套件介面或Java或Scala lambda函式來定義函式。 以下示例顯示如何對點選流進行會話並計算每個會話的點選次數。

      // 對點選流進行會話並計算每個會話的點選次數
      DataStream<Click> clicks = ...
      
      DataStream<Tuple2<String, Long>> result = clicks
        .map(
          new MapFunction<Click, Tuple2<String, Long>>() {
            @Override
            public Tuple2<String, Long> map(Click click) {
              return Tuple2.of(click.userId, 1L);
            }
          })
        // 定義userId的鍵是0
        .keyBy(0)
        // 定義30分鐘的會話間隙
        .window(EventTimeSessionWindows.withGap(Time.minutes(30L)))
        // 計算每個會話的點選數
        .reduce((a, b) -> Tuple2.of(a.f0, a.f1 + b.f1));複製程式碼

相關文章