一.基本算子
中文翻譯:
二.updateStateByKey
updateStateByKey操作可以保持任意狀態,同時不斷用新信息更新它。要使用此功能,必須執行兩個步驟。
- 定義狀態-狀態可以是任意數據類型。
- 定義狀態更新功能-使用功能指定如何使用輸入流中的先前狀態和新值來更新狀態。
在每個批次中,Spark都會對所有現有密鑰應用狀態更新功能,而不管它們是否在批次中具有新數據。如果更新函數返回,None將刪除鍵值對。
讓我們用一個例子來說明。假設要保持在文本數據流中看到的每個單詞的連續計數。此處,運行計數是狀態,它是整數。將更新函數定義爲:
def updateFunction(newValues: Seq[Int], runningCount: Option[Int]): Option[Int] = {
val newCount = ... // add the new values with the previous running count to get the new count
Some(newCount)
}
這適用於包含單詞的DStream【wordcount會把單詞映射爲(word, 1)對的DStream】。
調用如下:
val runningCounts = pairs.updateStateByKey[Int](updateFunction _)
將爲每個單詞調用更新函數,每個單詞newValues的序列爲1(來自各(word, 1)對),並且runningCount具有先前的計數。
請注意,使用updateStateByKey需要配置檢查點目錄。
ssc.checkpoint("D:\\checkpoint")
完整代碼:
package spark2.streaming
import org.apache.log4j.{Level, Logger}
import org.apache.spark.SparkConf
import org.apache.spark.streaming.{Seconds, StreamingContext}
/**
* Created by Administrator on 2020/5/7.
*/
object UpdateStateByKey {
/**
* 累加
* @param newValues 當前週期新數據的值
* @param runningCount 歷史累計值
* @return
*/
def updateFunction(newValues : Seq[Int], runningCount : Option[Int]) : Option[Int] = {
val preCount = runningCount.getOrElse(0)
val newCount = newValues.sum
Some(newCount + preCount) // 累加
}
Logger.getLogger("org").setLevel(Level.WARN) // 設置日誌級別
def main(args: Array[String]) {
val conf = new SparkConf().setMaster("local[2]").setAppName(s"${this.getClass.getSimpleName}")
val ssc = new StreamingContext(conf,Seconds(5))
// 設置檢測點
ssc.checkpoint("E:\\checkpoint")
val lines = ssc.socketTextStream("master",9999) // 與nc端口對應
val words = lines.flatMap(_.split(" "))
var pairs = words.map(word=>(word,1)).reduceByKey(_+_)
// 累加
pairs = pairs.updateStateByKey[Int](updateFunction _) // 必須設置檢查點
pairs.foreachRDD(row => row.foreach(println))
ssc.start()
ssc.awaitTermination()
ssc.stop()
}
}
執行結果:
三.Transform
transform操作【及其類似的變體transformWith】允許將任意RDD-to-RDD功能應用於DStream。它可用於應用DStream API中未公開的任何RDD操作。例如,將數據流中的每個rdd與另一個數據集連接在一起的功能未直接在DStream API中公開。但是,可以輕鬆地使用transform來執行此操作。這實現了非常強大的可能性。例如,可以通過將輸入數據流與預先計算的垃圾郵件信息【也可能由Spark生成】結合在一起,然後基於該信息進行過濾來進行實時數據清理。
val spamInfoRDD = ssc.sparkContext.newAPIHadoopRDD(...) // RDD containing spam information
val cleanedDStream = wordCounts.transform { rdd =>
rdd.join(spamInfoRDD).filter(...) // join data stream with spam information to do data cleaning
...
}
請注意,在每個批處理間隔中都會調用提供的函數。這使得可以執行隨時間變化的RDD操作,即可以在批之間更改RDD操作,分區數,廣播變量等。
完整代碼:
package spark2.streaming
import org.apache.log4j.{Level, Logger}
import org.apache.spark.sql.SparkSession
import org.apache.spark.streaming.{Seconds, StreamingContext}
/**
* Created by Administrator on 2020/6/23.
*/
object StreamingTransform {
Logger.getLogger("org").setLevel(Level.WARN) // 設置日誌級別
def main(args: Array[String]) {
val spark = SparkSession.builder()
.appName(s"${this.getClass.getSimpleName}")
.master("local[2]")
.getOrCreate()
val sc = spark.sparkContext
/**
* 生成數據集,黑名單,寫在ssc之前,避免多次執行
*/
val array = Array[String]("spark,1", "flink,0", "storm,0")
val filters = sc.parallelize(array).map(row => (row.split(",")(0), row.split(",")(1).toInt))
filters.count()
filters.foreach(println)
val ssc = new StreamingContext(sc,Seconds(5))
val lines = ssc.socketTextStream("master",9999) // 與nc端口對應
val words = lines.flatMap(_.split(" "))
val pairs = words.map(word=>(word, 1)).reduceByKey(_+_)
/**
* 關聯
*/
pairs.foreachRDD(rdd =>{
val mid = rdd.leftOuterJoin(filters).map(row =>{
val filter = row._2._2 match { // 模式匹配
case Some(a) => a
case None => 0
}
(row._1, row._2._1, filter)
})
.filter(_._3 == 1) // filter == 1
.map(row => (row._1, row._2))
mid.foreach(println)
})
ssc.start()
ssc.awaitTermination()
ssc.stop()
}
}
執行結果:
Using Spark's default log4j profile: org/apache/spark/log4j-defaults.properties
20/06/23 20:20:43 WARN NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
(flink,0)
(spark,1)
(storm,0)
20/06/23 20:21:01 WARN RandomBlockReplicationPolicy: Expecting 1 replicas with only 0 peer/s.
20/06/23 20:21:01 WARN BlockManager: Block input-0-1592914861200 replicated to only 0 peer(s) instead of 1 peers
(spark,1)
20/06/23 20:21:33 WARN RandomBlockReplicationPolicy: Expecting 1 replicas with only 0 peer/s.
20/06/23 20:21:33 WARN BlockManager: Block input-0-1592914893200 replicated to only 0 peer(s) instead of 1 peers
(spark,2)