Flink筆記02——單詞計數wc和集羣部署

前言

在上一篇文章Flink筆記01——入門篇講述了Flink的一些基礎知識後,這篇博客,我們結合日常開發,主要介紹一些Flink的基礎編程和框架搭建。

第一個Flink代碼

相信學過MR Spark的同學 編寫的第一個程序都是單詞計數word count,同理 這裏南國也是以單詞計數作爲開始。

開發環境

(由於之前的博客 很多時候忘記描述這個步驟,作爲該系列的基礎篇,這次儘可能的在每個地方描述細緻。)
開發IDE採用的是IDEA,編寫代碼前 安裝好Maven Scala插件,這些準備在IDEA內操作比較簡單,不做過多講述。

這裏主要講述一下配置maven依賴,在pom.xml中配置:

<properties>
        <flink.version>1.7.0</flink.version>
        <java.version>1.8</java.version>
        <scala.version>2.11.0</scala.version>
        <hadoop.version>2.6.0</hadoop.version>
    </properties>
    <dependencies>
        <!--flink基礎依賴-->
        <dependency>
            <groupId>org.apache.flink</groupId>
            <artifactId>flink-scala_2.11</artifactId>
            <version>${flink.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.flink</groupId>
            <artifactId>flink-streaming-scala_2.11</artifactId>
            <version>${flink.version}</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <!-- 該插件用於將Scala代碼編譯成class文件 -->
            <plugin>
                <groupId>net.alchim31.maven</groupId>
                <artifactId>scala-maven-plugin</artifactId>
                <version>3.4.6</version>
                <executions>
                    <execution>
                        <!-- 聲明綁定到maven的compile階段 -->
                        <goals>
                            <goal>testCompile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-assembly-plugin</artifactId>
                <version>3.0.0</version>
                <configuration>
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                </configuration>
                <executions>
                    <execution>
                        <id>make-assembly</id>
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

這裏因爲我是採用scala編程,因而添加的依賴選擇的是相關scala版本的包。如果是選擇Java編程,可在Flink官網上查找相關依賴配置。

流計算的demo(也就是上一篇文章 提到的無邊界數據)

package com.flink.primary

import org.apache.flink.streaming.api.scala.StreamExecutionEnvironment

/**
  * flink 流計算 wc
  * @author xjh 2020.4.4
  */
object FlinkStreamingWordCount {
  def main(args: Array[String]): Unit = {
    //1.初始化flink 流計算的環境
    val streamEnv: StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment
    //2.導入隱式轉換
    import org.apache.flink.streaming.api.scala._
    //3.讀取數據(這裏選擇socket數據)
    val stream: DataStream[String] = streamEnv.socketTextStream("m1", 8888)
    //DataStream類同於sparkStreaming中的DStream
    //4.轉換和處理數據
    val result = stream.flatMap(_.split(" "))
      .map((_, 1))
      .keyBy(0) //分組算子 0或者1代表前面DataStream的下標,0代表單詞 1代表出現的次數
      .sum(1) //聚合累加
    //5.打印結果
    result.print("結果")
    //6.啓動流計算程序
    streamEnv.execute("wordcount")
  }
}

代碼編寫使用的是Scala,注意查看這裏的初始化環境是StreamExecutionEnvirment類型,讀取數據的類型是DataStream(這個關鍵詞在日後的flink內容會多次看到)

批處理的demo(有邊界數據)

package com.flink.primary

import org.apache.flink.api.scala.{DataSet, ExecutionEnvironment}

/**
  * Flink 批處理Batch wordcount
  *
  * @author xjh 2020.4.4
  */
object FlinkBatchWordCount {
  def main(args: Array[String]): Unit = {
    //1.初始化flink批處理的環境
    val environment = ExecutionEnvironment.getExecutionEnvironment
    //2.導入隱式轉換
    import org.apache.flink.streaming.api.scala._

    val dataPath = getClass.getResource("/wordcount.txt")
    val data: DataSet[String] = environment.readTextFile(dataPath.getPath)
    //這裏的DataSet類同於Spark中的RDD

    //計算並打印結果
    data.flatMap(_.split(" ")).map((_, 1))
      .groupBy(0)
      .sum(1)
      .print()
  }
}

比較於上一次流計算的demo,這裏的初始化環境使用的是ExecutionEnvironment.getExecutionEnvironment,讀取數據源的類型是DataSet API,因爲是批處理,這裏不用啓動流處理程序。

需要說明的是,在Flink的應用中 更多的是做流計算,所以我們需要重點關注DataStream API的使用和掌握。

Flink集羣的基礎架構

Flink 整個系統主要由兩個組件組成,分別爲JobManager 和TaskManager,Flink 架構也遵循Master-Slave 架構設計原則,JobManager 爲Master 節點,TaskManager 爲Worker(Slave)節點。所有組件之間的通信都是藉助於Akka Framework,包括任務的狀態以及Checkpoint 觸發等信息。

在這裏插入圖片描述

  1. Client 客戶端
    客戶端負責將任務提交到集羣,與JobManager 構建Akka 連接,然後將任務提交到JobManager,通過和JobManager 之間進行交互獲取任務執行狀態。客戶端提交任務可以採用CLI 方式或者通過使用Flink WebUI 提交,也可以在應用程序中指定JobManager 的RPC網絡端口構建ExecutionEnvironment 提交Flink 應用。
  2. JobManager
    JobManager 負責整個Flink 集羣任務的調度以及資源的管理,從客戶端中獲取提交的應用,然後根據集羣中TaskManager 上TaskSlot 的使用情況,爲提交的應用分配相應的TaskSlots 資源並命令TaskManger 啓動從客戶端中獲取的應用。JobManager 相當於整個集羣的Master 節點,且整個集羣中有且僅有一個活躍的JobManager,負責整個集羣的任務管理和資源管理。JobManager 和TaskManager 之間通過Actor System 進行通信,獲取任務執行的情況並通過Actor System 將應用的任務執行情況發送給客戶端。同時在任務執行過程
    中,Flink JobManager 會觸發Checkpoints 操作,每個TaskManager 節點收到Checkpoint觸發指令後,完成Checkpoint 操作,所有的Checkpoint 協調過程都是在Flink JobManager中完成
    。當任務完成後,Flink 會將任務執行的信息反饋給客戶端,並且釋放掉TaskManager中的資源以供下一次提交任務使用。
  3. TaskManager
    TaskManager 相當於整個集羣的Slave 節點,負責具體的任務執行和對應任務在每個節點上的資源申請與管理。客戶端通過將編寫好的Flink 應用編譯打包,提交到JobManager,然後JobManager 會根據已經註冊在JobManager 中TaskManager 的資源情況,將任務分配給有資源的TaskManager 節點,然後啓動並運行任務。TaskManager 從JobManager 接收需要部署的任務,然後使用Slot 資源啓動Task,建立數據接入的網絡連接,接收數據並開始數據處理。同時TaskManager 之間的數據交互都是通過數據流的方式進行的

可以看出,Flink 的任務運行其實是採用多線程的方式,這和MapReduce 多JVM 進程的方式有很大的區別Fink 能夠極大提高CPU 使用效率**,在多個任務和Task 之間通過TaskSlot方式共享系統資源,每個TaskManager 中通過管理多個TaskSlot 資源池進行對資源進行有效管理**。

Flink的安裝部署模式

類似於Spark,Flink的安裝部署主要分爲本地模式和集羣模式。常見的部署模式如下:

  1. local模式
  2. Standalone模式
  3. Flink on Yarn模式

這裏有一點說明的是,現在的運用中還有Docker Kubernetes(K8s),不過在日常開發中運用的最多的是Yarn模式,因而關於Docker K8s的部署 暫時不做過多敘述。

local模式

local模式簡單,一般用於開發時對代碼的測試。部署時只需在主節點上解壓安裝包就代表成功安裝了,在flink安裝目錄下使用./bin/start-cluster.sh命令,就可以通過master:8081監控集羣狀態,關閉集羣命令:./bin/stop-cluster.sh。
開發者一般使用這種模式的場景 南國覺得大概就是Flink代碼在本地IDEA寫完之後 直接運行。

Standalone模式

  1. 進入conf目錄下,編輯flink-conf.yml
jobmanager.rpc.address: m1

# The RPC port where the JobManager is reachable.

jobmanager.rpc.port: 6123

# The heap size for the JobManager JVM

jobmanager.heap.size: 1024m

# The heap size for the TaskManager JVM

taskmanager.heap.size: 1024m

# The number of task slots that each TaskManager offers. Each slot runs one parallel pipeline.

taskmanager.numberOfTaskSlots: 3

# The parallelism used for programs that did not specify and other parallelism.

parallelism.default: 1

其中,jobmanager.rpc.address: m1表示指定Flink集羣的jobmanager的地址是m1;taskmanager.numberOfTaskSlots: 3表示每一個Taskmanager上有3個Slot.

  1. 編輯master文件
    m1:8081

  2. 編輯slaves文件
    m1
    m2
    m3

  3. 將修改好的配置分發到另外的服務器節點上
    scp -r /home/xjh/bigData/flink-1.7.0 xjh@m2:/home/xjh/bigData/
    scp -r /home/xjh/bigData/flink-1.7.0 xjh@m3:/home/xjh/bigData/

  4. 啓動flink集羣服務

[xjh@m1 flink-1.7.0]$ bin/start-cluster.sh 
Starting cluster.
Starting standalonesession daemon on host m1.
Starting taskexecutor daemon on host m1.
Starting taskexecutor daemon on host m2.
Starting taskexecutor daemon on host m3.
[xjh@m1 flink-1.7.0]$ jps
4728 StandaloneSessionClusterEntrypoint
5274 Jps
5212 TaskManagerRunner
  1. 訪問Flink 的WebUI
    在這裏插入圖片描述
    網址m1:8081 表示當前flink 集羣的jobmanager(master)的主機名和端口號,在這裏我們配置的是3個Taskamanager,每個Taskmanager上有3個Slot,即可以運行3個task,所以一共是9個Task Slot.
  2. 提交job到集羣上運行
    在flink中,提交作業有兩種方式:
  • Jar包指令上傳
    首先,打包:
    在這裏插入圖片描述
    依次對maven項目點擊clean complile package,則會在項目的Target中查看到打包後的Jar包:
    在這裏插入圖片描述
    將Jar包上傳到linux中指定的文件路徑。

然後,在linux中輸入指令:
bin/flink run -d -c com.flink.primary.StreamWordCount /home/xjh/flink-code/Flink-project-1.0-SNAPSHOT.jar

需要說明的是,如果Stream的流數據源是socket,則在在提交Jar包到Flink之前時打開程序中指定的主機和端口號

最後在WebUI上查看job的執行結果
在這裏插入圖片描述
在這裏插入圖片描述

  • Jar通過WebUI直接提交
    這種方式簡單便捷,也是Spark所沒有的。
    在這裏插入圖片描述
    查看job的執行過程和結果同上一種方法類似。

Flink on Yarn模式

這種模式的原理是依靠Yarn來調度Flink任務。這種模式的好處是可以充分利用集羣資源,提高集羣機器的利用率,並且只需要1 套Hadoop 集羣,就可以執行MapReduce 和Spark 任務,還可以執行Flink 任務等,操作非常方便,不
需要維護多套集羣,運維方面也很輕鬆。
Flink on Yarn的內部原理如下圖:
在這裏插入圖片描述

  • 當啓動一個新的Flink YARN Client 會話時,客戶端首先會檢查所請求的資源(容器和內存)是否可用。之後,它會上傳Flink 配置和JAR 文件到HDFS。
  • 客戶端的下一步是請求一個YARN 容器啓動ApplicationMaster 。JobManager 和ApplicationMaster(AM)運行在同一個容器中,一旦它們成功地啓動了,AM 就能夠知道JobManager 的地址,它會爲TaskManager 生成一個新的Flink 配置文件(這樣它才能連上JobManager),該文件也同樣會被上傳到HDFS。另外,AM 容器還提供了Flink 的Web 界面服務。Flink 用來提供服務的端口是由用戶和應用程序ID 作爲偏移配置的,這使得用戶能夠並行執行多個YARN 會話。
  • 之後,AM 開始爲Flink 的TaskManager 分配容器(Container),從HDFS 下載JAR 文件和修改過的配置文件。一旦這些步驟完成了,Flink 就安裝完成並準備接受任務了。

這裏值得注意的是啓動Flink on Yarn模式有兩個基礎條件:

  • 配置Hadoop集羣,併成功啓動
  • Flink的版本必須能和Hadoop兼容。官方的Flink版本從1.7.2之後的安裝包都沒有明確的指定兼容Hadoop版本號,因而需要下載Flink提交到Hadoop的連接器(Jar包) 如下圖所示,並將其拷貝到Flink的lib目錄下:
    在這裏插入圖片描述
    1.7.2及之前的版本在下載時可根據集羣上原有的scala版本和hadoop版本下載對應的安裝包,下圖所示:
    在這裏插入圖片描述

Flink在Yarn上使用分爲兩種模式:

Yarn-Session模式

在Yarn中提前初始化一個Flink集羣並開闢指定的資源,之後的Flink任務會提交到這裏執行。這個Flink集羣會常駐在Yarn集羣中,除非開發者手動停止。
缺點:這種模式創建的Flink集羣會獨佔資源,不管是否有Flink任務在執行,Yarn上面的其他任務都無法使用這些資源。
在這裏插入圖片描述
有的資料中會將這種模式稱之爲內存集中管理模式,大致意思也就是都有啓動的Flink Job都在在Flink yarn-session中管理並執行。yarn-session模式的執行概括起來分爲兩個步驟:yarn-session.sh(開闢資源)+flink run(提交任務)

  1. 首先啓動集羣上的Hadoop集羣,然後啓動一個Flink的yarn-session集羣。例如:bin/yarn-session.sh -n 3 -s 3 -nm Fink-project -d
其中yarn-session.sh 後面支持多個參數。下面針對一些常見的參數進行講解:
 -n,--container <arg> 表示分配容器的數量(也就是TaskManager 的數量)。
 -D <arg> 動態屬性。
 -d,--detached 在後臺獨立運行。
 -jm,--jobManagerMemory <arg>:設置JobManager 的內存,單位是MB。
 -nm,--name:在YARN 上爲一個自定義的應用設置一個名字。
 -q,--query:顯示YARN 中可用的資源(內存、cpu 核數)。
-qu,--queue <arg>:指定YARN 隊列。
 -s,--slots <arg>:每個TaskManager 使用的Slot 數量。
 -tm,--taskManagerMemory <arg>:每個TaskManager 的內存,單位是MB。
 -z,--zookeeperNamespace <arg>:針對HA 模式在ZooKeeper 上創建NameSpace。
 -id,--applicationId <yarnAppId>:指定YARN 集羣上的任務ID,附着到一個後臺獨
立運行的yarn session 中。

運行成功後,我們可以在yarn管理的主頁查看到該任務成功運行。
在這裏插入圖片描述
2. 查看Flink的WebUI
在這裏插入圖片描述
因爲沒有真實提交過Flink job,因而這裏的Task slots爲0。
3. 提交job到Flink yarn-session,因爲有了之前的操作,這裏提交Flink 作業時會自動提交到yarn中。
執行命令:bin/flink run -d -c com.flink.primary.StreamWordCount /home/xjh/flink-code/Flink-project-1.0-SNAPSHOT.jar
日誌運行中我們可以查看到提供的application id

查看Flink WebUI:
在這裏插入圖片描述

Yarn-Cluster模式

每次提交Flink任務都會創建一個新的Flink集羣,每個Flink任務之間相互獨立,互不影響。任務執行完成之後創建的Flink集羣也會消失。
優點:不會額外佔用資源,按需使用Yarn集羣的資源,這種方式下資源利用率遠高於yarn-session模式,推薦使用這種模式
在這裏插入圖片描述
yarn-cluster模式也可稱爲內存job管理模式。這種模式下不需要先啓動yarn-session。所以我們可以把前面啓動的yarn-session 集羣先停止,停止的命令是:
yarn application -kill application_1576832892572_0002
//其中application_1576832892572_0002 是ID
確保Hadoop 集羣是健康的情況下直接提交Job 命令:
bin/flink run -m yarn-cluster -yn 3 -ys 3 -ynm bjsxt02 -c
com.bjsxt.flink.StreamWordCount /home/Flink-Demo-1.0-SNAPSHOT.jar

任務提交參數講解:指定運行模式爲yarn-cluster。對比Yarn-Session 參數而言,只是前面加了y。

-yn,--container <arg> 表示分配容器的數量,也就是TaskManager 的數量。
 -d,--detached:設置在後臺運行。
 -yjm,--jobManagerMemory<arg>:設置JobManager 的內存,單位是MB。
 -ytm,--taskManagerMemory<arg>:設置每個TaskManager 的內存,單位是MB。
 -ynm,--name:給當前Flink application 在Yarn 上指定名稱。
 -yq,--query:顯示yarn 中可用的資源(內存、cpu 核數)
 -yqu,--queue<arg> :指定yarn 資源隊列
 -ys,--slots<arg> :每個TaskManager 使用的Slot 數量。
 -yz,--zookeeperNamespace<arg>:針對HA 模式在Zookeeper 上創建NameSpace
 -yid,--applicationID<yarnAppId> : 指定Yarn 集羣上的任務ID,附着到一個後臺獨
立運行的Yarn Session 中。

再次看到yarn的webUI:
在這裏插入圖片描述

Flink的HA

默認情況下,每個Flink 集羣只有一個JobManager,這將導致單點故障(SPOF),如
果這個JobManager 掛了,則不能提交新的任務,並且運行中的程序也會失敗。使用
JobManager HA,集羣可以從JobManager 故障中恢復,從而避免單點故障。用戶可以在
Standalone 或Flink on Yarn 集羣模式下配置Flink 集羣HA(高可用性)。
Standalone 模式下,JobManager 的高可用性的基本思想是,任何時候都有一個Alive
JobManager 和多個Standby JobManager
。Standby JobManager 可以在Alive JobManager
掛掉的情況下接管集羣成爲Alive JobManager,這樣避免了單點故障,一旦某一個Standby
JobManager 接管集羣,程序就可以繼續運行。Standby JobManagers 和Alive JobManager
實例之間沒有明確區別,每個JobManager 都可以成爲Alive 或Standby。

Flink集羣的HA配置

實現HA 還需要依賴ZooKeeper 和HDFS,因此要有一個ZooKeeper 集羣和Hadoop 集羣,
在這裏插入圖片描述

  1. 修改配置文件conf/masters
    前面的博客,masters我們只指定了m1,這裏我們要將所有的節點添加進來。
m1:8081
m2:8081
m3:8081
  1. 修改conf/flink-conf.yaml
#要啓用高可用,設置修改爲zookeeper
high-availability: zookeeper
#Zookeeper的主機名和端口信息,多個參數之間用逗號隔開
high-availability.zookeeper.quorum:
m1:2181,m2:2181,m3:2181
# 建議指定HDFS的全路徑。如果某個Flink節點沒有配置HDFS的話,不指定HDFS的全路徑
則無法識到,storageDir存儲了恢復一個JobManager所需的所有元數據。
high-availability.storageDir: hdfs://m1:9000/flink/ha
  1. scp把修改好的配置文件拷貝到其他服務器上
  2. 啓動集羣 bin/start-cluster.sh

Flink On Yarn配置

正常基於Yarn 提交Flink 程序,無論是使用yarn-session 模式還是yarn-cluster 模
式, 基於yarn 運行後的application 只要kill 掉對應的Flink 集羣進程
“YarnSessionClusterEntrypoint”後,基於Yarn 的Flink 任務就失敗了,不會自動進行
重試,所以基於Yarn 運行Flink 任務,也有必要搭建HA,這裏同樣還是需要藉助zookeeper
來完成,步驟如下:

  1. 修改所有Hadoop節點的yarn-site.xml,將所有Hadoop節點的yarn-site.xml中的提交應用程序的最大嘗試次數調大
#在每臺hadoop節點yarn-site.xml中設置提交應用程序的最大嘗試次數,建議不低於4,這裏
重試指的是ApplicationMaster
<property>
<name>yarn.resourcemanager.am.max-attempts</name>
<value>4</value>
</property>
  1. 啓動Hadoop集羣和Zookeeper集羣
  2. 修改Flink對應的flink-conf.yaml配置
#配置依賴zookeeper模式進行HA搭建
high-availability: zookeeper
#配置JobManager原數據存儲路徑
high-availability.storageDir: hdfs://m1:9000/flink/yarnha/
#配置zookeeper集羣節點
high-availability.zookeeper.quorum:
m1:2181,m2:2181,m3:2181
#yarn停止一個application重試的次數
yarn.application-attempts: 10
  1. 啓動yarn-session.sh測試HA:yarn-session.sh -n 2,當然也可以直接提交Job啓動之後,可以登錄yarn中對應的flink WebUI.
    在Flink WebUI中查看JobManager在哪臺節點上啓動,然後在對應的linux節點上,kill掉對應的“YarnSessionClusterEntrypoint”進程。
    最後進入到Yarn中觀察應用程序的job信息。點擊Job 會發現有對應的重試信息。

Flink並行度和Slot

Flink 中每一個worker(TaskManager)都是一個JVM 進程,它可能會在獨立的線程(Solt)
上執行一個或多個subtask。Flink 的每個TaskManager 爲集羣提供Solt
。Solt 的數量通常
與每個TaskManager 節點的可用CPU 內核數成比例,一般情況下Slot 的數量就是每個節點
的CPU 的核數。
Slot 的數量由集羣中flink-conf.yaml 配置文件中設置
taskmanager.numberOfTaskSlots 的值爲3,這個值的大小建議和節點CPU 的數量保持一致。
在這裏插入圖片描述
關於Slot 和parallelism 的區別,概括如下:

  1. Slot 是靜態的概念,是指TaskManager 具有的併發執行能力。
  2. parallelism 是動態的概念,是指程序運行時實際使用的併發能力。
  3. 設置合適的parallelism 能提高運算效率。

一個任務的並行度設置可以從4 個層面指定:

  • Operator Level(算子層面)。
  • Execution Environment Level(執行環境層面)。
  • Client Level(客戶端層面)。
  • System Level(系統層面)。
    這些並行度的優先級爲Operator Level>Execution Environment Level>Client
    Level>System Level。
    在這裏插入圖片描述
    在這裏插入圖片描述
    在這裏插入圖片描述
    在這裏插入圖片描述
    參考資料:
  1. Flink官網
  2. 尚學堂Flink教案
  3. Flink on Yarn的兩種模式及HA
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章