ApacheFlink? - 是針對於數據流的狀態計算,Flink具有特殊類DataSet和DataStream來表示程序中的數據。您可以將它們視爲可以包含重複項的不可變數據集合。在DataSet的情況下,數據是有限的,而對於DataStream,元素的數量可以是無限的。
這些集合在某些關鍵方面與常規Java集合不同。首先,它們是不可變的,這意味着一旦創建它們就無法添加或刪除元素。你也不能簡單地檢查裏面的元素。 最初通過在Flink程序中添加Source來創建集合,並通過使用諸如map,filter等API方法對它們進行轉換來從這些集合中派生新集合。
結構分析
Flink程序看起來像是轉換數據集合的常規程序。每個程序包含相同的基本部分:
- 獲得執行環境,
- 加載/創建初始數據,
- 指定此數據的轉換,
- 指定放置計算結果的位置,
- 觸發程序執行
獲取執行環境
Flink提供了三種運行Flink計算的方式:
- 遠程jar包部署方式
var streamEnv = StreamExecutionEnvironment.getExecutionEnvironment()
- 1
- 本地執行
var streamEnv = StreamExecutionEnvironment.createLocalEnvironment()
- 1
- 跨平臺提交
var streamEnv = StreamExecutionEnvironment.createRemoteEnvironment("CentOS",8081,"jarFiles")
- 1
用戶可以更具需求自行抉擇選擇哪種方式測試或者運行代碼。
加載/創建初始數據
創建需要加載的數據源,一般該數據源來源於消息隊列或者其他第三方系統,爲了測試方便這裏我們先使用socketTextStream
實現數據源的加載。
var source = streamEnv.socketTextStream("CentOS", 9999)
- 1
指定此數據的轉換
通過指定數據轉換規則對DataStream上應用轉換以創建新的派生DataStream。
source.flatMap(_.split("\\W+"))
.map((_,1))
.keyBy(0)
.sum(1)
- 1
- 2
- 3
- 4
將計算結果存儲到 文件中
writeAsCsv("D:/flinks/results")
- 1
觸發程序執行
streamEnv.execute("wordcounts")
- 1
將以上程序放置在一起
import org.apache.flink.streaming.api.scala.StreamExecutionEnvironment
import org.apache.flink.api.scala._
//1.創建流處理執行環境 - 本地環境
var streamEnv = StreamExecutionEnvironment.createLocalEnvironment()
//2.創建數據源
var source = streamEnv.socketTextStream(“CentOS”, 9999)
//3.數據轉換
source.flatMap(.split("\W+"))
.map((,1))
.keyBy(0)
.sum(1)
//4.指定輸出路徑
.writeAsCsv(“D:/flinks/results”, FileSystem.WriteMode.OVERWRITE)
//5.觸發任務執行
streamEnv.execute(“wordcounts”);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
Flink數據源
數據源是您的程序從中讀取輸入的位置。您可以使用StreamExecutionEnvironment.addSource(sourceFunction)將數據源附加到程序。 Flink附帶了許多預先實現的源函數,但您可以通過爲非並行源實現SourceFunction,或者通過實現ParallelSourceFunction接口或爲並行源擴展RichParallelSourceFunction來編寫自己的自定義源。
預定義的流源:
- readTextFile(path) - 讀取文本文件,即逐行遵循TextInputFormat規範的文件,並將它們作爲字符串返回。
- readFile(fileInputFormat,path) - 按指定的文件輸入格式指定讀取(一次)文件。
- readFile(fileInputFormat,path,watchType,interval,pathFilter,typeInfo) - 這是前兩個調用的內部方法。它根據給定的fileInputFormat讀取路徑中的文件。根據提供的watchType,此源可以定期監視(每隔ms)新數據的路徑(FileProcessingMode.PROCESS_CONTINUOUSLY),或者處理當前在路徑中的數據並退出(FileProcessingMode.PROCESS_ONCE)。使用pathFilter,用戶可以進一步排除處理文件。
在底層,Flink將文件讀取過程分爲兩個子任務,即目錄監控和數據讀取。這些子任務中的每一個都由單獨的實體實現。監視由單個非並行(並行度= 1)任務實現,而讀取由並行運行的多個任務執行。後者的並行性等於job的並行性。單個監視任務的作用是掃描目錄(定期或僅一次,具體取決於watchType),找到要處理的文件,將它們分成分割,並將這些分割分配給下游Reader。Reader是那些將讀取實際數據的任務。每個分割僅由一個讀取器讀取,而讀取器可以逐個讀取多個Split數據。
重要: 如果watchType設置爲FileProcessingMode.PROCESS_CONTINUOUSLY,則在修改文件時,將完全重新處理其內容。這可以打破“完全一次”的語義,因爲在文件末尾附加數據將導致其所有內容被重新處理。 如果watchType設置爲FileProcessingMode.PROCESS_ONCE,則源掃描路徑一次並退出,而不等待讀者完成讀取文件內容。當然Reader將繼續讀取文件數據,直到讀取所有文件內容。當讀取完成後,不再有檢查點保存。這可能會導致節點故障後恢復速度變慢,因爲作業將從上一個檢查點恢復讀取。
<!--HDFS依賴-->
<dependency>
<groupId>jdk.tools</groupId>
<artifactId>jdk.tools</artifactId>
<version>1.8</version>
<scope>system</scope>
<systemPath>D:/Program Files/Java/jdk1.8.0_121/lib/tools.jar</systemPath>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<version>${hadoop.version}</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-hdfs</artifactId>
<version>${hadoop.version}</version>
</dependency>
<!–Scala依賴–>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-scala_KaTeX parse error: Expected 'EOF', got '&' at position 102: …n punctuation">&̲lt;/</span>arti…{flink.version}</version>
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-streaming-scala_KaTeX parse error: Expected 'EOF', got '&' at position 102: …n punctuation">&̲lt;/</span>arti…{flink.version}</version>
</dependency>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- readTextFile
var streamEnv = StreamExecutionEnvironment.createLocalEnvironment()
var source = streamEnv.readTextFile("hdfs:///demo/words")
source.flatMap(_.split("\\W+"))
.map((_,1))
.keyBy(0)
.sum(1)
.writeAsCsv("D:/flinks/results", FileSystem.WriteMode.OVERWRITE)
streamEnv.execute(“wordcounts”);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- readFile
系統會每間隔10ms檢測目錄,如果有新文件產生立即採集,如果文本內容發生變化,立即重新讀取。
var streamEnv = StreamExecutionEnvironment.createLocalEnvironment()
var inputFormat=new TextInputFormat(null)
var source = streamEnv.readFile(
inputFormat,
"hdfs:///demo/words",
FileProcessingMode.PROCESS_CONTINUOUSLY,
10,
new FilePathFilter {
override def filterPath(path: Path): Boolean = {
return !path.getPath().contains("log")
}
}
)
source.flatMap(_.split("\\W+"))
.map((_,1))
.keyBy(0)
.sum(1)
.print()
streamEnv.execute(“wordcounts”)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
Socket-based
從套接字讀取。元素可以用分隔符分隔。
var streamEnv = StreamExecutionEnvironment.createLocalEnvironment()
var source = streamEnv.socketTextStream("CentOS", 9999)
source.flatMap(_.split("\\W+"))
.map((_,1))
.keyBy(0)
.sum(1)
.writeAsCsv("D:/flinks/results", FileSystem.WriteMode.OVERWRITE)
streamEnv.execute(“wordcounts”);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
Collection-based
- fromCollection(Collection) - 從Java Java.util.Collection創建數據流。集合中的所有元素必須屬於同一類型。
var streamEnv = StreamExecutionEnvironment.createLocalEnvironment()
var source =streamEnv.fromCollection(List("this is a demo hello world"))
source.flatMap(_.split("\\W+"))
.map((_,1))
.keyBy(0)
.sum(1)
.print()
streamEnv.execute(“wordcounts”)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- fromElements(T …) - 根據給定的對象序列創建數據流。所有對象必須屬於同一類型。
var streamEnv = StreamExecutionEnvironment.createLocalEnvironment()
var source =streamEnv.fromElements("this is a demo","good good study")
source.flatMap(_.split("\\W+"))
.map((_,1))
.keyBy(0)
.sum(1)
.print()
streamEnv.execute(“wordcounts”)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- fromCollection(Iterator,Class) - 從迭代器創建數據流。該類指定迭代器返回的元素的數據類型。
var streamEnv = StreamExecutionEnvironment.createLocalEnvironment()
var source =streamEnv.fromCollection(Iterator(User(1,"zhangsan",true),
User(2,"lisi",false),
User(3,"ww",true)))
source.map(user=>(user.sex,1))
.keyBy(0)
.sum(1)
.print()
streamEnv.execute(“usersexcount”)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- generateSequence(from,to) - 並行生成給定時間間隔內的數字序列。
var streamEnv = StreamExecutionEnvironment.createLocalEnvironment()
var source =streamEnv.generateSequence(0,1000)
source.filter(_%2==0)
.print()
streamEnv.execute(“計算偶數”)
- 1
- 2
- 3
- 4
- 5
- 6
addSource(流處理)
添加新的源功能。例如,要從Apache Kafka讀取,您可以使用addSource(new FlinkKafkaConsumer <>(…))
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-connector-kafka_${flink.scala.version}</artifactId>
<version>${flink.version}</version>
</dependency>
- 1
- 2
- 3
- 4
- 5
val env = StreamExecutionEnvironment.getExecutionEnvironment
val props = new Properties()
props.setProperty(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "CentOS:9092")
props.setProperty(ConsumerConfig.GROUP_ID_CONFIG, "g1")
env.addSource(new FlinkKafkaConsumer[String](“topic01”,new SimpleStringSchema(),props))
.flatMap(line => for( i <- line.split(" ")) yield (i,1))
.keyBy(_._1)
.reduce((in1,in2)=>(in1._1,in1._2+in2._2))
.print()
env.execute(“kafka message count”)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
注意在測試機器上必須配置主機名和IP的映射關係,否則系統聯繫不上Kafka服務器。
更多精彩內容關注
</div>