關注公眾號:
大資料技術派
,回覆資料
,領取1024G
資料。
Flink系列文章
第01講:Flink 的應用場景和架構模型
第02講:Flink 入門程式 WordCount 和 SQL 實現
我們右鍵執行時相當於在本地啟動了一個單機版本。生產中都是叢集環境,並且是高可用的,生產上提交任務需要用到flink run 命令,指定必要的引數。
本課時我們主要介紹 Flink 的入門程式以及 SQL 形式的實現。
上一課時已經講解了 Flink 的常用應用場景和架構模型設計,這一課時我們將會從一個最簡單的 WordCount 案例作為切入點,並且同時使用 SQL 方式進行實現,為後面的實戰課程打好基礎。
我們首先會從環境搭建入手,介紹如何搭建本地除錯環境的腳手架;然後分別從DataSet(批處理)和 DataStream(流處理)兩種方式如何進行單詞計數開發;最後介紹 Flink Table 和 SQL 的使用。
Flink 開發環境
通常來講,任何一門大資料框架在實際生產環境中都是以叢集的形式執行,而我們除錯程式碼大多數會在本地搭建一個模板工程,Flink 也不例外。
Flink 一個以 Java 及 Scala 作為開發語言的開源大資料專案,通常我們推薦使用 Java 來作為開發語言,Maven 作為編譯和包管理工具進行專案構建和編譯。對於大多數開發者而言,JDK、Maven 和 Git 這三個開發工具是必不可少的。
關於 JDK、Maven 和 Git 的安裝建議如下表所示:
工程建立
一般來說,我們在通過 IDE 建立工程,可以自己新建工程,新增 Maven 依賴,或者直接用 mvn 命令建立應用:
複製程式碼
mvn archetype:generate \
-DarchetypeGroupId=org.apache.flink \
-DarchetypeArtifactId=flink-quickstart-java \
-DarchetypeVersion=1.10.0
通過指定 Maven 工程的三要素,即 GroupId、ArtifactId、Version 來建立一個新的工程。同時 Flink 給我提供了更為方便的建立 Flink 工程的方法:
複製程式碼
curl https://flink.apache.org/q/quickstart.sh | bash -s 1.10.0
我們在終端直接執行該命令:
直接出現 Build Success 資訊,我們可以在本地目錄看到一個已經生成好的名為 quickstart 的工程。
這裡需要的主要的是,自動生成的專案 pom.xml 檔案中對於 Flink 的依賴註釋掉 scope:
複製程式碼
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-java</artifactId>
<version>${flink.version}</version>
<!--<scope>provided</scope>-->
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-streaming-java_${scala.binary.version}</artifactId>
<version>${flink.version}</version>
<!--<scope>provided</scope>-->
</dependency>
DataSet WordCount
WordCount 程式是大資料處理框架的入門程式,俗稱“單詞計數”。用來統計一段文字每個單詞的出現次數,該程式主要分為兩個部分:一部分是將文字拆分成單詞;另一部分是單詞進行分組計數並列印輸出結果。
整體程式碼實現如下:
複製程式碼
public static void main(String[] args) throws Exception {
// 建立Flink執行的上下文環境
final ExecutionEnvironment env = ExecutionEnvironment.getExecutionEnvironment();
// 建立DataSet,這裡我們的輸入是一行一行的文字
DataSet<String> text = env.fromElements(
"Flink Spark Storm",
"Flink Flink Flink",
"Spark Spark Spark",
"Storm Storm Storm"
);
// 通過Flink內建的轉換函式進行計算
DataSet<Tuple2<String, Integer>> counts =
text.flatMap(new LineSplitter())
.groupBy(0)
.sum(1);
//結果列印
counts.printToErr();
}
public static final class LineSplitter implements FlatMapFunction<String, Tuple2<String, Integer>> {
@Override
public void flatMap(String value, Collector<Tuple2<String, Integer>> out) {
// 將文字分割
String[] tokens = value.toLowerCase().split("\\W+");
for (String token : tokens) {
if (token.length() > 0) {
out.collect(new Tuple2<String, Integer>(token, 1));
}
}
}
}
實現的整個過程中分為以下幾個步驟。
首先,我們需要建立 Flink 的上下文執行環境:
複製程式碼
ExecutionEnvironment env = ExecutionEnvironment.getExecutionEnvironment();
然後,使用 fromElements 函式建立一個 DataSet 物件,該物件中包含了我們的輸入,使用 FlatMap、GroupBy、SUM 函式進行轉換。
最後,直接在控制檯列印輸出。
我們可以直接右鍵執行一下 main 方法,在控制檯會出現我們列印的計算結果:
DataStream WordCount
為了模仿一個流式計算環境,我們選擇監聽一個本地的 Socket 埠,並且使用 Flink 中的滾動視窗,每 5 秒列印一次計算結果。程式碼如下:
複製程式碼
public class StreamingJob {
public static void main(String[] args) throws Exception {
// 建立Flink的流式計算環境
final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
// 監聽本地9000埠
DataStream<String> text = env.socketTextStream("127.0.0.1", 9000, "\n");
// 將接收的資料進行拆分,分組,視窗計算並且進行聚合輸出
DataStream<WordWithCount> windowCounts = text
.flatMap(new FlatMapFunction<String, WordWithCount>() {
@Override
public void flatMap(String value, Collector<WordWithCount> out) {
for (String word : value.split("\\s")) {
out.collect(new WordWithCount(word, 1L));
}
}
})
.keyBy("word")
.timeWindow(Time.seconds(5), Time.seconds(1))
.reduce(new ReduceFunction<WordWithCount>() {
@Override
public WordWithCount reduce(WordWithCount a, WordWithCount b) {
return new WordWithCount(a.word, a.count + b.count);
}
});
// 列印結果
windowCounts.print().setParallelism(1);
env.execute("Socket Window WordCount");
}
// Data type for words with count
public static class WordWithCount {
public String word;
public long count;
public WordWithCount() {}
public WordWithCount(String word, long count) {
this.word = word;
this.count = count;
}
@Override
public String toString() {
return word + " : " + count;
}
}
}
整個流式計算的過程分為以下幾步。
首先建立一個流式計算環境:
複製程式碼
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
然後進行監聽本地 9000 埠,將接收的資料進行拆分、分組、視窗計算並且進行聚合輸出。程式碼中使用了 Flink 的視窗函式,我們在後面的課程中將詳細講解。
我們在本地使用 netcat 命令啟動一個埠:
複製程式碼
nc -lk 9000
然後直接執行我們的 main 方法:
可以看到,工程啟動後開始監聽 127.0.0.1 的 9000 埠。
在 nc 中輸入:
複製程式碼
$ nc -lk 9000
Flink Flink Flink
Flink Spark Storm
可以在控制檯看到:
複製程式碼
Flink : 4
Spark : 1
Storm : 1
Flink Table & SQL WordCount
Flink SQL 是 Flink 實時計算為簡化計算模型,降低使用者使用實時計算門檻而設計的一套符合標準 SQL 語義的開發語言。
一個完整的 Flink SQL 編寫的程式包括如下三部分。
- Source Operator:是對外部資料來源的抽象, 目前 Apache Flink 內建了很多常用的資料來源實現,比如 MySQL、Kafka 等。
- Transformation Operators:運算元操作主要完成比如查詢、聚合操作等,目前 Flink SQL 支援了 Union、Join、Projection、Difference、Intersection 及 window 等大多數傳統資料庫支援的操作。
- Sink Operator:是對外結果表的抽象,目前 Apache Flink 也內建了很多常用的結果表的抽象,比如 Kafka Sink 等。
我們也是通過用一個最經典的 WordCount 程式作為入門,上面已經通過 DataSet/DataStream API 開發,那麼實現同樣的 WordCount 功能, Flink Table & SQL 核心只需要一行程式碼:
複製程式碼
//省略掉初始化環境等公共程式碼
SELECT word, COUNT(word) FROM table GROUP BY word;
首先,整個工程中我們 pom 中的依賴如下圖所示:
複製程式碼
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-java</artifactId>
<version>1.10.0</version>
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-streaming-java_2.11
<version>1.10.0</version>
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-table-api-java-bridge_2.11</artifactId>
<version>1.10.0</version>
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-table-planner-blink_2.11</artifactId>
<version>1.10.0</version>
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-table-planner_2.11</artifactId>
<version>1.10.0</version>
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-table-api-scala-bridge_2.11</artifactId>
<version>1.10.0</version>
</dependency>
第一步,建立上下文環境:
複製程式碼
ExecutionEnvironment fbEnv = ExecutionEnvironment.getExecutionEnvironment();
BatchTableEnvironment fbTableEnv = BatchTableEnvironment.create(fbEnv);
第二步,讀取一行模擬資料作為輸入:
複製程式碼
String words = "hello flink hello lagou";
String[] split = words.split("\\W+");
ArrayList<WC> list = new ArrayList<>();
for(String word : split){
WC wc = new WC(word,1);
list.add(wc);
}
DataSet<WC> input = fbEnv.fromCollection(list);
第三步,註冊成表,執行 SQL,然後輸出:
複製程式碼
//DataSet 轉sql, 指定欄位名
Table table = fbTableEnv.fromDataSet(input, "word,frequency");
table.printSchema();
//註冊為一個表
fbTableEnv.createTemporaryView("WordCount", table);
Table table02 = fbTableEnv.sqlQuery("select word as word, sum(frequency) as frequency from WordCount GROUP BY word");
//將錶轉換DataSet
DataSet<WC> ds3 = fbTableEnv.toDataSet(table02, WC.class);
ds3.printToErr();
整體程式碼結構如下:
複製程式碼
public class WordCountSQL {
public static void main(String[] args) throws Exception{
//獲取執行環境
ExecutionEnvironment fbEnv = ExecutionEnvironment.getExecutionEnvironment();
//建立一個tableEnvironment
BatchTableEnvironment fbTableEnv = BatchTableEnvironment.create(fbEnv);
String words = "hello flink hello lagou";
String[] split = words.split("\\W+");
ArrayList<WC> list = new ArrayList<>();
for(String word : split){
WC wc = new WC(word,1);
list.add(wc);
}
DataSet<WC> input = fbEnv.fromCollection(list);
//DataSet 轉sql, 指定欄位名
Table table = fbTableEnv.fromDataSet(input, "word,frequency");
table.printSchema();
//註冊為一個表
fbTableEnv.createTemporaryView("WordCount", table);
Table table02 = fbTableEnv.sqlQuery("select word as word, sum(frequency) as frequency from WordCount GROUP BY word");
//將錶轉換DataSet
DataSet<WC> ds3 = fbTableEnv.toDataSet(table02, WC.class);
ds3.printToErr();
}
public static class WC {
public String word;
public long frequency;
public WC() {}
public WC(String word, long frequency) {
this.word = word;
this.frequency = frequency;
}
@Override
public String toString() {
return word + ", " + frequency;
}
}
}
我們直接執行該程式,在控制檯可以看到輸出結果:
總結
本課時介紹了 Flink 的工程建立,如何搭建除錯環境的腳手架,同時以 WordCount 單詞計數這一最簡單最經典的場景用 Flink 進行了實現。第一次體驗了 Flink SQL 的強大之處,讓你有一個直觀的認識,為後續內容打好基礎。