SparkStreaming常見transformation算子

  • map (func)
    對DStream中的各個元素進行func函數操作,然後返回一個新的DStream

  • flatMap (func)
    與map方法類似,只不過各個輸入項可以被輸出爲零個或多個輸出項

  • filter (func)
    過濾出所有函數func返回值爲true的DStream元素並返回一個新的DStream

  • repartition (numPartitions)
    增加或減少DStream中的分區數,從而改變DStream的並行度

  • union (otherStream)
    將源DStream和輸入參數爲otherDStream的元素合併,並返回一個新的DStream.

  • count()

    通過對DStreaim中 的各個RDD中的元素進行計數,然後返回只有一個元素的RDD構成的DStream

  • reduce (func)
    對源DStream中的各個RDD中的元素利用func進行聚合操作,然後返回只有一個元素的RDD構成的新的DStream

  • countByValue ()
    對於元素類型爲K的DStream,返回一個元素爲(K, Long)鍵值對形式的新的DStream, Long對應的值爲源DStream中各個
    RDD的key出現的次數

  • reduceByKey (func,[nomTasks])
    利用func函數對源DStream中的key進行聚合操作,然後返回新的(K,V)對構成的DStream

  • join (otherStream, [numTasks] )
    輸入爲(K,V)、(K,W) 類型的DStream, 返回一個新的(K,(v,w)類型的DStream

  • cogroup (otherStream,
    [nonmTasks])
    輸入爲(K,V)、 (K,W) 類型的DStream,返回一個新的(K,Seq[V], Seq[W]) 元組類型的
    DStream

    • cogroup就是groupByKey的另外一種變體,groupByKey是操作一個K-V鍵值對,而cogroup一次操作兩個,有點像join,不同之處在於返回值結果

      val ds1:DStream[(K, V)]
      val ds2:DStream[(K, W)]
      val cg:DStream[(K, (Iterable[V], Iterable[W]))] = ds1.cogroup(ds1)
      
  • transform (func)
    通過RDD-to-RDD函數作用於源碼DStream中的各個RDD,可以是任意的RDD操作,從而返回一個新的RDD

  • updateStateByKey (func)
    根據於key的前置狀態和key的新值,對key進行更新, 返回一個新狀態的Dstream

  • Window函數

    window窗口操作,每個多長M時間,通過過往N長時間內產生的數據

    這裏主要介紹最後三個算子,其他算子用法見https://blog.csdn.net/a805814077/article/details/103063182

下面所有的案例均在機器上使用nc命令打開9999端口發送消息來進行測試

nc -lk hadoop01 9999

1.transform

transform是一個transformation算子,轉換算子。DStream上述提供的所有的transformation操作,都是DStream to DStream操作,沒有一個DStream和RDD的直接操作,而DStream本質上是一系列RDD,所以RDD to RDD操作是顯然被需要的,所以此時官方api中提供了一個爲了達成此操作的算子——transform操作。

其最最最經典的實現就是DStream和rdd的join操作,還有dstream重分區(分區減少,coalsce)。

也就是說transform主要就是用來自定義官方api沒有提供的一些操作。

Transform.scala

package sparkstreaming

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

/**
  * @Author Daniel
  * @Description transform算子
  **/
object Transform {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf()
      .setAppName("Transform")
      .setMaster("local[*]")
    val batchInterval = Seconds(2)
    val ssc = new StreamingContext(conf, batchInterval)

    val lines = ssc.socketTextStream("hadoop01", 9999)

    //在transform中使用flatMap、map、reduceByKey、coalesce算子
    val words = lines.transform(rdd => rdd.flatMap(_.split("\\s+")))
    val pairs = words.transform(rdd => rdd.map((_, 1)))
    val res = pairs.transform(rdd => rdd.reduceByKey(_ + _))
    //使用coalesce算子減少分區
    res.transform(_.coalesce(2))
    res.print()
    ssc.start()
    ssc.awaitTermination()
  }
}

2.updateStateByKey

這個算子一般不建議使用。根據於key的前置狀態和key的新值,對key進行更新,返回一個新狀態的Dstream。就是統計截止到目前爲止key的狀態。

主要步驟如下:

  1. 定義狀態:可以是任意數據類型
  2. 定義狀態更新函數:用一個函數指定如何使用先前的狀態,從輸入流中的新值更新狀態。對於有狀態的操作,要不斷的把當前和歷史的時間切片的RDD累加計算,隨着時間的流失,計算的數據規模會變得越來越大
  3. 要思考的是如果數據量很大的時候,或者對性能的要求極爲苛刻的情況下,可以考慮將數據放在Redis或者tachyon或者ignite上
  4. 注意,updateStateByKey操作,要求必須開啓Checkpoint機制。

UpdateStateByKey.scala

package sparkstreaming

import org.apache.spark.SparkConf
import org.apache.spark.streaming.{Seconds, StreamingContext, Time}

/**
  * @Author Daniel
  * @Description UpdateStateByKey算子
  **/
object UpdateStateByKey {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf()
      .setAppName("_04UpdateStateByKeyOps")
      .setMaster("local[*]")
    val batchInterval = Seconds(2)
    val ssc = new StreamingContext(conf, batchInterval)
    //必須開啓checkpoint機制
    ssc.checkpoint("file:///F:/ssdata/checkpoint/ck1")
    val lines = ssc.socketTextStream("hadoop01", 9999)
    val words = lines.transform(rdd => rdd.flatMap(_.split("\\s+")))
    val pairs = words.transform(rdd => rdd.map((_, 1)))
    val ret = pairs.transform(rdd => rdd.reduceByKey(_ + _))
    //依賴兩個狀態:一者前置狀態,一者當前狀態
    val usb = ret.updateStateByKey(updateFunc)

    usb.print()

    ssc.start()
    ssc.awaitTermination()
  }

  //狀態更新函數,聚合截止目前爲止所有key的狀態
  def updateFunc(seq: Seq[Int], option: Option[Int]): Option[Int] = {
    //seq爲當前key的狀態,option爲key對應的歷史值
    Option(seq.sum + option.getOrElse(0))
  }
}

3.window

window操作就是窗口函數。Spark Streaming提供了滑動窗口操作的支持,從而讓我們可以對一個滑動窗口內的數據執行計算操作。每次掉落在窗口內的RDD的數據,會被聚合起來執行計算操作,然後生成的RDD,會作爲window DStream的一個RDD。比如下圖中,就是對每三秒鐘的數據執行一次滑動窗口計算,這3秒內的3個RDD會被聚合起來進行處理,然後過了兩秒鐘,又會對最近三秒內的數據執行滑動窗口計算。所以每個滑動窗口操作,都必須指定兩個參數,窗口長度以及滑動間隔,而且這兩個參數值都必須是batch間隔的整數倍

在這裏插入圖片描述

紅色的矩形就是一個窗口,窗口框住的是一段時間內的數據流。

這裏面每一個time都是時間單元,在官方的例子中,每隔window size是3 time unit, 而且每隔2個單位時間,窗口會slide一次。
所以基於窗口的操作,需要指定2個參數:
window length - The duration of the window (3 in the figure)
slide interval - The interval at which the window-based operation is performed (2 in the figure).

簡而言之,window窗口操作就是每過M時間,計算N長時間內產生的數據,M就是滑動長度,N就是窗口長度。

Window.scala

package sparkstreaming

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

/**
  * @Author Daniel
  * @Description Window算子
  **/
object Window {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf()
      .setAppName("Window")
      .setMaster("local[*]")
    val batchInterval = 2
    val ssc = new StreamingContext(conf, Seconds(batchInterval))
    val lines = ssc.socketTextStream("hadoop01", 9999)
    val words = lines.transform(rdd => rdd.flatMap(_.split("\\s+")))
    val pairs = words.transform(rdd => rdd.map((_, 1)))
    //每隔4s,統計過去6s單位內產生的數據
    val windowDuration = Seconds(batchInterval * 3)
    val slideDuration = Seconds(batchInterval * 2)
    //windowDuration就是窗口長度,slideDuration就是滑動長度
    val ret = pairs.reduceByKeyAndWindow((v1: Int, v2: Int) => v1 + v2, windowDuration, slideDuration)
    ret.print()
    ssc.start()
    ssc.awaitTermination()
  }

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