Spark Streaming:reduceByKeyAndWindow、foreachRDD算子

基於Window窗口的轉化操作

基於窗口的操作會在一個比StreamingContext 的批次間隔更長的時間範圍內,通過整合多個批次的結果,計算出整個窗口的結果。

滑動窗口轉換操作的計算過程如下圖所示,我們可以事先設定一個滑動窗口的長度(也就是窗口的持續時間),並且設定滑動窗口的時間間隔(每隔多長時間執行一次計算),然後,就可以讓窗口按照指定時間間隔在源DStream上滑動,每次窗口停放的位置上,都會有一部分DStream的數據被框入窗口內,形成一個小段的DStream,這時,就可以啓動對這個小段DStream的計算。

在這裏插入圖片描述

所有基於窗口的操作都需要兩個參數,分別爲窗口時長以及滑動步長,兩者都必須是
StreamContext 的批次間隔的整數倍。窗口時長控制每次計算最近的多少個批次的數據,其實就是最近的windowDuration/batchInterval 個批次。如果有一個以10 秒爲批次間隔的源DStream,要創建一個最近30 秒的時間窗口(即最近3 個批次),就應當把windowDuration設爲30 秒。而滑動步長的默認值與批次間隔相等,用來控制對新的DStream 進行計算的間隔。如果源DStream 批次間隔爲10 秒,並且我們只希望每兩個批次計算一次窗口結果,就應該把滑動步長設置爲20 秒。

對DStream 可以用的最簡單窗口操作是window(),它返回一個新的DStream 來表示所請求的窗口操作的結果數據。換句話說,window() 生成的DStream 中的每個RDD 會包含多個批次中的數據,可以對這些數據進行count()、transform() 等操作。
在這裏插入圖片描述

如上圖,假設批次的時間間隔爲t1,第一列爲輸入DStream,第二列爲經過窗口操作的DStream,每次掉落在窗口內的批次數據(RDD),會被聚合起來執行計算操作,然後生成的RDD,會作爲window DStream的一個RDD。當時間爲t2時,已經過了兩個批次,此時將最近2個批次的數據聚合起來進行窗口操作;從t2又經過了兩個批次後來到t4,此時將最近的3個批次即t2、t3和t4的數據聚合起來進行窗口操作;從t4又經過兩個批次後來到t6,將最近的t4、t5和t6批次的數據聚合經窗口操作形成新的window DStream,最後window DStream包含3個RDD。

val accessLogsWindow = accessLogsDStream.window(Seconds(30), Seconds(10))
val windowCounts = accessLogsWindow.count() // 統計windowDStream中每個RDD的元素數量

儘管可以使用window() 寫出所有的窗口操作,Spark Streaming 還是提供了一些其他的窗口操作,讓用戶可以高效而方便地使用。

首先,reduceByWindow() 和reduceByKeyAndWindow()讓我們可以對每個窗口更高效地進行歸約操作。它們接收一個歸約函數,在整個窗口上執行,比如+。reduceByWindow()操作的示意圖如下:窗口時長爲4,滑動時長爲1
在這裏插入圖片描述

reduceByKeyAndWindow實現熱點搜索詞滑動統計

reduceByKeyAndWindow(func, windowLength, slideInterval, [numTasks]) 應用到一個(K,V)鍵值對組成的DStream上時,會返回一個由(K,V)鍵值對組成的新的DStream。每一個key的值均由給定的reduce函數(func函數)進行聚合計算。注意:在默認情況下,這個算子利用了Spark默認的併發任務數去分組。可以通過numTasks參數的設置來指定不同的任務數。

案例:熱點搜索詞滑動統計,每隔10s,統計最近60s內搜索詞的搜索頻次,並打印出排名最靠前的3個搜索詞以及其出現次數

package com.spark_streaming

import org.apache.log4j.{Level, Logger}
import org.apache.spark.SparkConf
import org.apache.spark.streaming.{Seconds, StreamingContext}

object windowHotWord {
  def main(args: Array[String]): Unit = {
    val conf  = new SparkConf().setMaster("local[2]").setAppName("windowHotWord")
    val ssc = new StreamingContext(conf, Seconds(5))
    
    Logger.getLogger("org.apache.spark").setLevel(Level.ERROR)
    
    val searchLogDStream = ssc.socketTextStream("master", 9999)
    val searchWordDStream = searchLogDStream.map(_.split(" ")(1))
    val searchWordPairsDStream = searchWordDStream.map((_, 1))
   

    // 計算window內搜索詞出現的次數
    val searchWordCountDStream = searchWordPairsDStream.reduceByKeyAndWindow(
      (a:Int, b:Int) => a + b,
      Seconds(60),
      Seconds(10))

    // 計算window內排名前3
    searchWordCountDStream.foreachRDD(searchWordCountRdd => {
      val countSearchWordRDD = searchWordCountRdd.map(tuple => (tuple._2, tuple._1)) //Tuple(Int, String)
      val sortedSearcWordRDD = countSearchWordRDD.sortByKey(false)
      val sortedWordCountRDD = sortedSearcWordRDD.map(tuple => (tuple._2, tuple._1))
      val top3HotWordRDD = sortedWordCountRDD.take(3)
      println("-----top start----")
      for(tuple <- top3HotWordRDD)
        println(tuple)
      println("-----top end -----")
    })

    ssc.start()
    ssc.awaitTermination()
  }
}

在這裏插入圖片描述
在使用reduceByKeyAndWindow()計算出搜索詞的出現次數後使用了一個輸出操作foreachRDD(),它用來對DStream中的RDD運行任意計算。在foreachRDD()中,可以重用在Spark core中的所有行動操作,這裏是對RDD內的數據作排序取出前三。

輸入測試數據:

[root@master ~]# nc -lk 9999
zhang scala
li hadoop
wang spark
li jaav
zhang hadoop
li java
wang spark
sun spark
zhao hadoop
wang java
li scala
zhang java
qian spak
wang hadoop
zhnag java
zhang spark
liu scala

輸出結果:

Using Spark's default log4j profile: org/apache/spark/log4j-defaults.properties
19/06/22 17:06:13 INFO SparkContext: Running Spark version 2.0.2
19/06/22 17:06:16 INFO SecurityManager: Changing view acls to: zby
19/06/22 17:06:16 INFO SecurityManager: Changing modify acls to: zby
19/06/22 17:06:16 INFO SecurityManager: Changing view acls groups to: 
19/06/22 17:06:16 INFO SecurityManager: Changing modify acls groups to: 
19/06/22 17:06:16 INFO SecurityManager: SecurityManager: authentication disabled; ui acls disabled; users  with view permissions: Set(zby); groups with view permissions: Set(); users  with modify permissions: Set(zby); groups with modify permissions: Set()
19/06/22 17:06:17 INFO Utils: Successfully started service 'sparkDriver' on port 51328.
19/06/22 17:06:17 INFO SparkEnv: Registering MapOutputTracker
19/06/22 17:06:17 INFO SparkEnv: Registering BlockManagerMaster
19/06/22 17:06:17 INFO DiskBlockManager: Created local directory at C:\Users\zby\AppData\Local\Temp\blockmgr-fe6e9b7c-0564-497d-a1a5-37adaa45581d
19/06/22 17:06:17 INFO MemoryStore: MemoryStore started with capacity 867.6 MB
19/06/22 17:06:17 INFO SparkEnv: Registering OutputCommitCoordinator
19/06/22 17:06:18 INFO Utils: Successfully started service 'SparkUI' on port 4040.
19/06/22 17:06:18 INFO SparkUI: Bound SparkUI to 0.0.0.0, and started at http://192.168.47.1:4040
19/06/22 17:06:18 INFO Executor: Starting executor ID driver on host localhost
19/06/22 17:06:18 INFO Utils: Successfully started service 'org.apache.spark.network.netty.NettyBlockTransferService' on port 51341.
19/06/22 17:06:18 INFO NettyBlockTransferService: Server created on 192.168.47.1:51341
19/06/22 17:06:18 INFO BlockManagerMaster: Registering BlockManager BlockManagerId(driver, 192.168.47.1, 51341)
19/06/22 17:06:18 INFO BlockManagerMasterEndpoint: Registering block manager 192.168.47.1:51341 with 867.6 MB RAM, BlockManagerId(driver, 192.168.47.1, 51341)
19/06/22 17:06:18 INFO BlockManagerMaster: Registered BlockManager BlockManagerId(driver, 192.168.47.1, 51341)
-----top start----
-----top end -----
-----top start----
-----top end -----
-----top start----
(scala,1)
-----top end -----
-----top start----
(scala,1)
(jaav,1)
(spark,1)
-----top end -----
-----top start----
(spark,3)
(hadoop,2)
(scala,1)
-----top end -----
-----top start----
(spark,3)
(hadoop,3)
(scala,2)
-----top end -----
-----top start----
(java,3)
(spark,3)
(hadoop,3)
-----top end -----
-----top start----
(java,4)
(spark,4)
(hadoop,4)
-----top end -----
-----top start----
(java,4)
(spark,4)
(hadoop,4)
-----top end -----
-----top start----
(java,4)
(spark,3)
(hadoop,3)
-----top end -----
-----top start----
(java,3)
(scala,2)
(hadoop,2)
-----top end -----
-----top start----
(java,2)
(scala,1)
(spak,1)
-----top end -----
-----top start----
(scala,1)
(java,1)
(spark,1)
-----top end -----
-----top start----
(scala,1)
-----top end -----
-----top start----
-----top end -----

Process finished with exit code 1

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章