Window Operations
先來看看什麼叫窗口滑動
def main(args: Array[String]): Unit = {
val list=Array(1,2,3,4,5,6);
val window=list.sliding(2);
for (r<-window){
println(r.mkString(";"));
}
}
-----------------
1;2
2;3
3;4
4;5
5;6
可以想像,有一個窗口,把兩個數包圍起來,然後一步一步的往後滑動
window Operations
window Operations可以設置窗口的大小和滑動窗口的間隔來動態的獲取當前Steaming的允許狀態。基於窗口的操作會在一個比 StreamingContext 的批次間隔更長的時間範圍內,通過整合多個批次的結果,計算出整個窗口的結果。
上圖可以觀察到,窗口的大小爲三,每次往後滑動兩個步長
- 注意:所有基於窗口的操作都需要兩個參數,分別爲窗口時長以及滑動步長,兩者都必須是 StreamContext 的批次間隔的整數倍。
- 窗口時長控制每次計算最近的多少個批次的數據,其實就是最近的 windowDuration/batchInterval 個批次。如果有一個以 10 秒爲批次間隔的源 DStream,要創建一個最近 30 秒的時間窗口(即最近 3 個批次),就應當把 windowDuration 設爲 30 秒。而滑動步長的默認值與批次間隔相等,用來控制對新的 DStream 進行計算的間隔。如果源 DStream 批次間隔爲 10 秒,並且我們只希望每兩個批次計算一次窗口結果, 就應該把滑動步長設置爲 20 秒。
- 假設,你想拓展前例從而每隔十秒對持續30秒的數據生成word count。爲做到這個,我們需要在持續30秒數據的(word,1)對DStream上應用reduceByKey。使用操作reduceByKeyAndWindow.
WordCount第三版:3秒一個批次,窗口12秒,滑步6秒。
def main(args: Array[String]) {
val conf = new SparkConf().setMaster("local[2]").setAppName("NetworkWordCount")
val ssc = new StreamingContext(conf, Seconds(3))
ssc.checkpoint(".")
// Create a DStream that will connect to hostname:port, like localhost:9999
val lines = ssc.socketTextStream("localhost", 9999)
// Split each line into words
val words = lines.flatMap(_.split(" "))
// Count each word in each batch
val pairs = words.map(word => (word, 1))
val wordCounts = pairs.reduceByKeyAndWindow((a:Int,b:Int) => (a + b),Seconds(12), Seconds(6))
// Print the first ten elements of each RDD generated in this DStream to the console
wordCounts.print()
ssc.start() // Start the computation
ssc.awaitTermination() // Wait for the computation to terminate
}
-------------------------------------
結果:
-------------------------------------------
Time: 1589966829000 ms
-------------------------------------------
(bbb,1)
(ccc,1)
(aaa,2)
-------------------------------------------
Time: 1589966835000 ms
-------------------------------------------
(ssss,1)
(bbb,2)
(ddd,1)
(lll,1)
(ccc,1)
(aaa,2)
-------------------------------------------
Time: 1589966841000 ms
-------------------------------------------
(ssss,1)
(bbb,2)
(ddd,1)
(lll,1)
(aaa,1)
-------------------------------------------
Time: 1589966847000 ms
-------------------------------------------
(bbb,1)
(aaa,1)
-------------------------------------------
Time: 1589966853000 ms
-------------------------------------------
Process finished with exit code -1
UpdateStateByKey
updateStateByKey操作使得我們可以在用新信息進行更新時保持任意的狀態。爲使用這個功能,你需要做下面兩步:
- 定義狀態,狀態可以是一個任意的數據類型。
- 定義狀態更新函數,用此函數闡明如何使用之前的狀態和來自輸入流的新值對狀態進行更新。
使用updateStateByKey需要對檢查點目錄進行配置,會使用檢查點來保存狀態。
更新版的wordcount:
ef main(args: Array[String]) {
定義更新狀態方法,參數values爲當前批次單詞頻度,state爲以往批次單詞頻度
val updateFunc = (values: Seq[Int], state: Option[Int]) => {
val currentCount = values.foldLeft(0)(_ + _)
val previousCount = state.getOrElse(0)
Some(currentCount + previousCount)
}
val conf = new SparkConf().setMaster("local[2]").setAppName("NetworkWordCount")
val ssc = new StreamingContext(conf, Seconds(3))
ssc.checkpoint("hdfs://localhost:9000/streamCheck")
val lines = ssc.socketTextStream("hadoop102", 9999)
// Split each line into words
val words = lines.flatMap(_.split(" "))
//import org.apache.spark.streaming.StreamingContext._ // not necessary since Spark 1.3
// Count each word in each batch
val pairs = words.map(word => (word, 1))
// 使用updateStateByKey來更新狀態,統計從運行開始以來單詞總的次數
val stateDstream = pairs.updateStateByKey[Int](updateFunc)
stateDstream.print()
ssc.start() // Start the computation
ssc.awaitTermination() // Wait for the computation to terminate
}
(2)啓動程序並向9999端口發送數據
nc -lk 9999
ni shi shui
ni hao ma
(3)結果展示
-------------------------------------------
Time: 1504685175000 ms
-------------------------------------------
-------------------------------------------
Time: 1504685181000 ms
-------------------------------------------
(shi,1)
(shui,1)
(ni,1)
-------------------------------------------
Time: 1504685187000 ms
-------------------------------------------
(shi,1)
(ma,1)
(hao,1)
(shui,1)
(ni,2)