-
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的狀態。
主要步驟如下:
- 定義狀態:可以是任意數據類型
- 定義狀態更新函數:用一個函數指定如何使用先前的狀態,從輸入流中的新值更新狀態。對於有狀態的操作,要不斷的把當前和歷史的時間切片的RDD累加計算,隨着時間的流失,計算的數據規模會變得越來越大
- 要思考的是如果數據量很大的時候,或者對性能的要求極爲苛刻的情況下,可以考慮將數據放在Redis或者tachyon或者ignite上
- 注意,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()
}
}