Flink DataStream 程式設計入門

渡碼發表於2019-06-19

流處理是 Flink 的核心,流處理的資料集用 DataStream 表示。資料流從可以從各種各樣的資料來源中建立(訊息佇列、Socket 和 檔案等),經過 DataStream 的各種 transform 操作,最終輸出檔案或者標準輸出。這個過程跟之前文章中介紹的 Flink 程式基本骨架一樣。本篇介紹 DataStream 相關的入門知識。

Flink 101

為了學習 Flink 的朋友能檢視到每個例子的原始碼,我建立了一個 GitHub 專案:https://github.com/duma-repo/awesome-flink 這裡會存放每一篇文章比較重要的示例的原始碼,目前支援 Java 和 Scala,仍在不斷完善中。程式碼下載後可以在本地執行,也可以打包放在叢集上執行。同時,歡迎各位將優質的資源提交到專案中。

簡單示例

import org.apache.flink.api.common.functions.FlatMapFunction;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.windowing.time.Time;
import org.apache.flink.util.Collector;

public class WindowWordCount {

    public static void main(String[] args) throws Exception {

        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();

        DataStream<Tuple2<String, Integer>> dataStream = env
                .socketTextStream("localhost", 9999)
                .flatMap(new Splitter())
                .keyBy(0)
                .timeWindow(Time.seconds(5))
                .sum(1);

        dataStream.print();

        env.execute("Window WordCount");
    }

    public static class Splitter implements FlatMapFunction<String, Tuple2<String, Integer>> {
        @Override
        public void flatMap(String sentence, Collector<Tuple2<String, Integer>> out) throws Exception {
            for (String word: sentence.split(" ")) {
                out.collect(new Tuple2<String, Integer>(word, 1)); //空格分割後,每個單詞轉換成 (word, 1) 二元組輸出
            }
        }
    }

}

這個例子跟之間介紹 WordCount 的例子類似,這裡詳細介紹下涉及的 API 和含義

  • 資料來源:socketTextStream 是從 socket 建立的資料流,可以使用 nc -l 9000 建立 socket 客戶端傳送資料
  • transform:flatMap 將輸入的資料按照空格分割後,扁平化處理(flat即為扁平的意思);keyBy 會按照指定的 key 進行分組,這裡就是將單詞作為 key;timeWindow 指定時間視窗,這裡是 5s 處理一次;sum 是聚合函式,將分組好的單詞個數求和
  • 輸出:print 將處理完的資料輸出到標準輸出流中,可以在控制檯看到輸出的結果。呼叫 execute 方法提交 Job

Data Source

經過以上的介紹,我們知道常見的資料來源有 socket、訊息佇列和檔案等。對於常見的資料來源 Flink 已經定義好了讀取函式,接下來一一介紹。

基於檔案

  • readTextFile(path):讀文字檔案,預設是檔案型別是 TextInputFormat,並且返回型別是 String
  • readFile(fileInputFormat, path):讀檔案,需要指定輸入檔案的格式
  • readFile(fileInputFormat, path, watchType, interval, typeInfo):以上兩個方法內部都會呼叫這個方法,引數說明:
    •  fileInputFormat - 輸入檔案的型別
    •  path - 輸入檔案路徑
    •  watchType - 取值為 FileProcessingMode.PROCESS_CONTINUOUSLY 和 FileProcessingMode.PROCESS_ONCE
      • FileProcessingMode.PROCESS_CONTINUOUSLY - 當輸入路徑下有檔案被修改,整個路徑下內容將會被重新處理
      • FileProcessingMode.PROCESS_ONCE - 只掃描一次,便退出。因此這種模式下輸入資料只讀取一次
    •  interval - 依賴 watchType 引數,對於 FileProcessingMode.PROCESS_CONTINUOUSLY 每隔固定時間(單位:毫秒)檢測路徑下是否有新資料
    •  typeInfo - 返回資料的型別

需要注意,在底層 Flink 將讀檔案的過程分為兩個子任務 —— 檔案監控和資料讀取(reader)。監控任務由 1 個 task 實現,而讀取的任務由多個 task 實現,數量與 Job 的並行度相同。監控任務的作用是掃描輸入路徑(週期性或者只掃描一次,取決於 watchType),當資料可以被處理時,會將資料分割成多個分片,將分片分配給下游的 reader 。一個分片只會被一個 reader 讀取,一個 reader 可以讀取多個分片。

基於 Socket

  • socketTextStream:從 socket 資料流中讀資料

基於 Collection

  • fromCollection(Collection):從 Java.util.Collection 型別的資料中建立輸入流,collection 中的所有元素型別必須相同
  • fromCollection(Iterator, Class):從 iterator (迭代器)中建立輸入流,Class 引數指定從 iterator 中的資料型別
  • fromElements(T ...):從給定的引數中建立輸入流, 所有引數型別必須相同
  • fromParallelCollection(SplittableIterator, Class):從 iterator 中建立並行的輸入流,Class 指定 iterator 中的資料型別
  • generateSequence(from, to):從 from 至 to 之間的資料序列建立並行的資料流

自定義

  • addSource:可以自定義輸入源,通過實現 SourceFunction 介面來自定義非並行的輸入流;也可以實現 ParallelSourceFunction 介面或整合 RichParallelSourceFunction 類來自定義並行輸入流,當然也可以定義好的資料來源,如:Kafka,addSource(new FlinkKafkaConsumer08<>(...))

DataStream 的 transform 

之前已經介紹了一些 transfrom 函式,如:map、flatMap 和 filter 等。同時還有視窗函式:window、timeWindow 等,聚合函式:sum、reduce 等。更多的 transform 函式以及使用將會單獨寫一篇文章介紹。

Data Sink

Data Sink 便是資料的輸出。同 Data Source 類似, Flink 也內建了一些輸出函式,如下:

 

  • writeAsText(path) / TextOutputFormat:將資料作為 String 型別輸出到指定檔案
  • writeAsCsv(...) / CsvOutputFormat:將 Tuple 型別輸出到 ',' 分隔的 csv 型別的檔案。行和列的分隔符可以通過引數配置,預設的為 '\n' 和 ','
  • print() / printToErr():將資料列印到標準輸出流或者標準錯誤流,可以指定列印的字首。
  • writeUsingOutputFormat() / FileOutputFormat:輸出到 OutputFormat 型別指定的檔案,支援物件到位元組的轉換。
  • writeToSocket:根據 SerializationSchema 將資料輸出到 socket 
  • addSink:自定義輸出函式,如:自定義將資料輸出到 Kafka

小結

本篇文章主要介紹了 Flink Streaming 程式設計的基本骨架。詳細介紹了 Streaming 內建的 Data Source 和 DataSink 。下篇將繼續介紹 Flink Streaming 程式設計涉及的基本概念。

程式碼地址: https://github.com/duma-repo/awesome-flink/blob/master/chapter-2-flink-streaming/2-1-streaming-starter.md

 

歡迎關注公眾號「渡碼」

 

 

相關文章