1、sparkStreaming概述
1.1 什麼是sparkStreaming
- Spark Streaming makes it easy to build scalable fault-tolerant streaming applications.
- sparkStreaming是一個可以非常容易的構建可擴展、具有容錯機制的流式應用程序
- 它就是一個實時處理的程序,數據源源不斷的來,然後它就進行實時不斷的處理。
1.2 sparkStreaming特性
-
1、易用性
-
可以像開發離線批處理一樣去編寫實時處理的代碼程序
-
可以使用多種不同的語言進行代碼開發
-
java
-
scala
-
python
-
-
-
2、容錯性
-
sparkStreaming可以實現恰好一次語義
- 數據被處理且只被處理一次
- 避免了數據丟失和數據的重複處理
- 數據被處理且只被處理一次
-
可以實現在沒有額外代碼的情況下來恢復一些丟失的工作和狀態
-
-
3、可以融合到spark生態系統
- sparkStreaming流式處理可以與批處理和交互式查詢進行結合使用
2、sparkStreaming原理
2.1 sparkStreaming計算原理
Spark Streaming 是基於spark的流式批處理引擎,其基本原理是把輸入數據以某一時間間隔批量的處理,當批處理間隔縮短到秒級時,便可以用於處理實時數據流。
2.2 sparkStreaming計算流程
sparkStreaming是某一個時間間隔的批處理,在時間維度上就劃分成了很多job,每一個job都有大量的Dstream,
對Dstream做大量的transformation轉換操作,其本質是作用在它內部的rdd上。
也就是說Dstream內部是封裝了rdd,rdd內部又有很多個分區,分區裏面纔是真正的數據。
2.3 sparkStreaming容錯性
(1)Dstream中內部是封裝了rdd,rdd自身是具有容錯機制,就是通過lineage血統來實現某些rdd的分區數據丟失之後,然後進行重新計算恢復得到。
(2)同時對於網絡數據的處理,sparkStreaming在接受到數據之後它會把網絡中的數據保留多份到其他機器,保證數據源端的安全性。
sc.textFile("/words.txt").flatMap(_.split(" ")).map((_,1)).reduceByKey(_+_).collect
rdd1 ----------------->rdd2--------->rdd3------------->rdd4
某個rdd的數據丟失之後恢復邏輯:血統+數據源
2.4 sparkStreaming實時性
storm是來一條數據就處理一條,實時性是比較高
sparkStreaming是以某一時間間隔的批量處理,它的實時性就比較低,延遲就比較高
後期再實際公司中具體使用哪一種框架,需要結合自身的一些業務場景
比如說公司的領導允許數據出現一定的延遲,對數據的實時性要求不是特別高,這個時候可以優先考慮sparkStreaming;
如果對數據的實時性非常高,就考慮storm。
3、DStream介紹
3.1 什麼是DStream
Discretized Stream是Spark Streaming的基礎抽象,代表持續性的數據流和經過各種Spark算子操作後的結果數據流。在內部實現上,DStream是一系列連續的RDD來表示。每個RDD含有一段時間間隔內的數據.
3.2 DStream上的操作分類
- transformation(轉換)
- 可以把一個DStream轉換生成一個新的Dstream,它也是延遲加載,不會立即觸發任務的運行
- 類似於rdd中transformation操作
- 可以把一個DStream轉換生成一個新的Dstream,它也是延遲加載,不會立即觸發任務的運行
- outputOperation (輸出)
- 它會觸發任務的真正運行
- 類似於rdd中action操作
- 它會觸發任務的真正運行
4、DStream操作實戰
-
添加依賴
<dependency> <groupId>org.apache.spark</groupId> <artifactId>spark-streaming_2.11</artifactId> <version>2.1.3</version> </dependency>
4.1 利用sparkStreaming接受socket數據實現單詞統計
- 1、代碼開發
package cn.itcast.socket
import org.apache.spark.streaming.dstream.{DStream, ReceiverInputDStream}
import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.streaming.{Seconds, StreamingContext}
//todo:利用sparkStreaming接受socket數據實現單詞統計
object SparkStreamingSocket {
def main(args: Array[String]): Unit = {
//1、創建SparkConf
val sparkConf: SparkConf = new SparkConf().setAppName("SparkStreamingSocket").setMaster("local[2]")
//2、創建SparkContext
val sc = new SparkContext(sparkConf)
sc.setLogLevel("warn")
//3、創建StreamingContext 需要一個SparkContext對象,還有一個批處理時間間隔 表示每隔5s處理上一個5s的數據
val ssc = new StreamingContext(sc,Seconds(5))
//4、接受socket數據
val socketTextStream: ReceiverInputDStream[String] = ssc.socketTextStream("node1",9999)
//5、切分每一行數據
val words: DStream[String] = socketTextStream.flatMap(_.split(" "))
//6、每個單詞計爲1
val wordAndOne: DStream[(String, Int)] = words.map((_,1))
//7、相同單詞出現的1累加
val result: DStream[(String, Int)] = wordAndOne.reduceByKey(_+_)
//8、打印輸出
result.print()
//9、開啓流式計算
ssc.start()
ssc.awaitTermination()
}
}
4.2 利用sparkStreaming接受socket數據實現所有批次單詞統計的結果累加
- 1、代碼開發
- updateStateByKey
- 可以按照key去更新狀態,key—>單詞 狀態—>次數
- updateStateByKey
package cn.itcast.socket
import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.streaming.{Seconds, StreamingContext}
import org.apache.spark.streaming.dstream.{DStream, ReceiverInputDStream}
//todo:利用sparkStreaming接受socket數據實現所有批次單詞統計的結果累加
object SparkStreamingSocketTotal {
//currentValues:當前批次相同的單詞出現的所有的1 (hadoop,1)(hadoop,1)(hadoop,1)----->List(1,1,1)
//historyValues: 在之前所有批次中相同單詞出現的總次數 Option類型可以表示有值(Some)或者是沒有值(None)
def updateFunc(currentValues:Seq[Int],historyValues:Option[Int]):Option[Int] = {
val newValue: Int = currentValues.sum + historyValues.getOrElse(0)
Some(newValue)
}
def main(args: Array[String]): Unit = {
//1、創建SparkConf
val sparkConf: SparkConf = new SparkConf().setAppName("SparkStreamingSocketTotal").setMaster("local[2]")
//2、創建SparkContext
val sc = new SparkContext(sparkConf)
sc.setLogLevel("warn")
//3、創建StreamingContext 需要一個SparkContext對象,還有一個批處理時間間隔 表示每隔5s處理上一個5s的數據
val ssc = new StreamingContext(sc,Seconds(5))
//設置ckeckpoint目錄,用於保存每一個單詞在之前的所有批次中出現的總次數
ssc.checkpoint("./socket")
//4、接受socket數據
val socketTextStream: ReceiverInputDStream[String] = ssc.socketTextStream("node1",9999)
//5、切分每一行數據
val words: DStream[String] = socketTextStream.flatMap(_.split(" "))
//6、每個單詞計爲1
val wordAndOne: DStream[(String, Int)] = words.map((_,1))
//7、相同單詞出現的1累加
val result: DStream[(String, Int)] = wordAndOne.updateStateByKey(updateFunc)
//8、打印輸出
result.print()
//9、開啓流式計算
ssc.start()
ssc.awaitTermination()
}
}
4.3 利用sparkStreaming接受socket數據實現單詞統計–使用開窗函數
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-gba23OsM-1572241319579)(reduceByKeyAndWindow使用介紹.png)]
- 1、代碼開發
- reduceByKeyAndWindow
package cn.itcast.socket
import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.streaming.{Seconds, StreamingContext}
import org.apache.spark.streaming.dstream.{DStream, ReceiverInputDStream}
//todo:利用sparkStreaming接受socket數據實現單詞統計---使用開窗函數
object SparkStreamingSocketWindow {
def main(args: Array[String]): Unit = {
//1、創建SparkConf
val sparkConf: SparkConf = new SparkConf().setAppName("SparkStreamingSocketWindow").setMaster("local[2]")
//2、創建SparkContext
val sc = new SparkContext(sparkConf)
sc.setLogLevel("warn")
//3、創建StreamingContext 需要一個SparkContext對象,還有一個批處理時間間隔 表示每隔5s處理上一個5s的數據
val ssc = new StreamingContext(sc,Seconds(5))
//4、接受socket數據
val socketTextStream: ReceiverInputDStream[String] = ssc.socketTextStream("node1",9999)
//5、切分每一行數據
val words: DStream[String] = socketTextStream.flatMap(_.split(" "))
//6、每個單詞計爲1
val wordAndOne: DStream[(String, Int)] = words.map((_,1))
//7、相同單詞出現的1累加
//reduceFunc: (V, V) => V, 就是一個函數
//windowDuration: Duration, 窗口的長度
//slideDuration: Duration 滑動窗口的時間間隔,它表示每隔多久計算一次
val result: DStream[(String, Int)] = wordAndOne.reduceByKeyAndWindow((x:Int,y:Int)=>x+y,Seconds(10),Seconds(10))
//8、打印輸出
result.print()
//9、開啓流式計算
ssc.start()
ssc.awaitTermination()
}
}
4.4 利用sparkStreaming接受socket數據實現一定時間內的熱門詞彙
-
1、代碼開發
-
transform
- 實現把一個Dstream轉換生成一個新的Dstream,內部需要一個函數,函數的輸入參數就是前面Dstream中的rdd,函數返回值是一個新的RDD
-
foreachRDD
- 它是一個outputOperation,會觸發任務的運行,需要一個函數,函數的輸入參數還是前面Dstream中的rdd,最後函數的返回值是Unit表示沒有
package cn.itcast.socket
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.streaming.{Seconds, StreamingContext}
import org.apache.spark.streaming.dstream.{DStream, ReceiverInputDStream}
//todo:利用sparkStreaming接受socket數據實現一定時間內熱門詞彙統計
object SparkStreamingSocketWindowHotWord {
def main(args: Array[String]): Unit = {
//1、創建SparkConf
val sparkConf: SparkConf = new SparkConf().setAppName("SparkStreamingSocketWindowHotWord").setMaster("local[2]")
//2、創建SparkContext
val sc = new SparkContext(sparkConf)
sc.setLogLevel("warn")
//3、創建StreamingContext 需要一個SparkContext對象,還有一個批處理時間間隔 表示每隔5s處理上一個5s的數據
val ssc = new StreamingContext(sc,Seconds(5))
//4、接受socket數據
val socketTextStream: ReceiverInputDStream[String] = ssc.socketTextStream("node1",9999)
//5、切分每一行數據
val words: DStream[String] = socketTextStream.flatMap(_.split(" "))
//6、每個單詞計爲1
val wordAndOne: DStream[(String, Int)] = words.map((_,1))
//7、相同單詞出現的1累加
//reduceFunc: (V, V) => V, 就是一個函數
//windowDuration: Duration, 窗口的長度
//slideDuration: Duration 滑動窗口的時間間隔,它表示每隔多久計算一次
val result: DStream[(String, Int)] = wordAndOne.reduceByKeyAndWindow((x:Int,y:Int)=>x+y,Seconds(10),Seconds(10))
//8、按照單詞出現的次數降序
val finalResult: DStream[(String, Int)] = result.transform(rdd => {
//可以使用rdd中排序的方法去操作
val sortRDD: RDD[(String, Int)] = rdd.sortBy(_._2, false)
//取出出現次數最多的前3位
val top3: Array[(String, Int)] = sortRDD.take(3)
//打印
println("===============top3 start===============")
top3.foreach(println)
println("===============top3 end=================")
sortRDD
})
//8、打印輸出
finalResult.print()
//9、開啓流式計算
ssc.start()
ssc.awaitTermination()
}
}
5、sparkStreaming整合flume
5.1 poll拉模式(優先考慮)
-
1、需要把spark-streaming-flume-sink_2.11-2.1.3.jar拷貝到flume/lib
-
2、還需要把flume中自帶的scala依賴由2.10改成2.11
-
3、flume配置文件
- flume-poll-spark.conf
#source a1.sources.r1.channels = c1 a1.sources.r1.type = spooldir a1.sources.r1.spoolDir = /root/data a1.sources.r1.fileHeader = true #channel a1.channels.c1.type =memory a1.channels.c1.capacity = 20000 a1.channels.c1.transactionCapacity=5000 #sinks a1.sinks.k1.channel = c1 a1.sinks.k1.type = org.apache.spark.streaming.flume.sink.SparkSink a1.sinks.k1.hostname=node1 a1.sinks.k1.port = 8888 a1.sinks.k1.batchSize= 2000
-
4、可以啓動flume
bin/flume-ng agent -n a1 -c conf -f conf/flume-poll-spark.conf -Dflume.root.logger=info,console
-
5、sparkStreaming代碼開發
-
引入依賴
<dependency> <groupId>org.apache.spark</groupId> <artifactId>spark-streaming-flume_2.11</artifactId> <version>2.1.3</version> </dependency>
-
代碼開發
package cn.itcast.flume import java.net.InetSocketAddress import org.apache.spark.storage.StorageLevel import org.apache.spark.streaming.dstream.{DStream, ReceiverInputDStream} import org.apache.spark.streaming.{Seconds, StreamingContext} import org.apache.spark.{SparkConf, SparkContext} import org.apache.spark.streaming.flume.{FlumeUtils, SparkFlumeEvent} //todo:sparkStreaming整合flume--------poll拉模式 object SparkStreamingPollFlume { def main(args: Array[String]): Unit = { //1、創建SparkConf val sparkConf: SparkConf = new SparkConf().setAppName("SparkStreamingPollFlume").setMaster("local[2]") //2、創建SparkContext val sc = new SparkContext(sparkConf) sc.setLogLevel("warn") //3、創建StreamingContext val ssc = new StreamingContext(sc,Seconds(5)) //4、通過poll拉模式獲取flume數據 val pollingStream: ReceiverInputDStream[SparkFlumeEvent] = FlumeUtils.createPollingStream(ssc,"node1",8888) //拉取多個flume收集到的數據 // val address=List(new InetSocketAddress("node1",8888),new InetSocketAddress("node2",8888),new InetSocketAddress("node3",8888)) // val totalFlume: ReceiverInputDStream[SparkFlumeEvent] = FlumeUtils.createPollingStream(ssc,address,StorageLevel.MEMORY_AND_DISK_SER_2) //flume中數據傳輸的最小單元:event event中有什麼? headers body // event:{"headers":xxxxx,"body":xxxxxxx} //5、獲取flume中的真實數據 new String(Array[byte]) val data: DStream[String] = pollingStream.map(x=>new String(x.event.getBody.array())) //6、切分每一行獲取所有的單詞 val words: DStream[String] = data.flatMap(_.split(" ")) //7、每個單詞計爲1 val wordAndOne: DStream[(String, Int)] = words.map((_,1)) //8、相同單詞出現的1累加 val result: DStream[(String, Int)] = wordAndOne.reduceByKey(_+_) //9、打印 result.print() //10 開啓流式計算 ssc.start() ssc.awaitTermination() } }
-
5.2 push推模式
-
1、修改flume配置
- vim flume-push-spark.conf
#push mode a1.sources = r1 a1.sinks = k1 a1.channels = c1 #source a1.sources.r1.channels = c1 a1.sources.r1.type = spooldir a1.sources.r1.spoolDir = /root/data a1.sources.r1.fileHeader = true #channel a1.channels.c1.type =memory a1.channels.c1.capacity = 20000 a1.channels.c1.transactionCapacity=5000 #sinks a1.sinks.k1.channel = c1 a1.sinks.k1.type = avro a1.sinks.k1.hostname=192.168.160.34 a1.sinks.k1.port = 8888 a1.sinks.k1.batchSize= 2000
-
2、代碼開發
package cn.itcast.flume import org.apache.spark.streaming.dstream.{DStream, ReceiverInputDStream} import org.apache.spark.{SparkConf, SparkContext} import org.apache.spark.streaming.{Seconds, StreamingContext} import org.apache.spark.streaming.flume.{FlumeUtils, SparkFlumeEvent} //todo:sparkStreaming整合flume--------push推模式 object SparkStreamingPushFlume { def main(args: Array[String]): Unit = { //1、創建SparkConf val sparkConf: SparkConf = new SparkConf().setAppName("SparkStreamingPushFlume").setMaster("local[2]") //2、創建SparkContext val sc = new SparkContext(sparkConf) sc.setLogLevel("warn") //3、創建StreamingContext val ssc = new StreamingContext(sc,Seconds(5)) //4、通過push推模式 接受flume的數據,這裏的hostname和port端口是sparkStreaming應用程序所在服務器地址和端口 val stream: ReceiverInputDStream[SparkFlumeEvent] = FlumeUtils.createStream(ssc,"192.168.160.34",8888) //5、獲取flume中的真實數據 new String(Array[byte]) val data: DStream[String] = stream.map(x=>new String(x.event.getBody.array())) //6、切分每一行獲取所有的單詞 val words: DStream[String] = data.flatMap(_.split(" ")) //7、每個單詞計爲1 val wordAndOne: DStream[(String, Int)] = words.map((_,1)) //8、相同單詞出現的1累加 val result: DStream[(String, Int)] = wordAndOne.reduceByKey(_+_) //9、打印 result.print() //10 開啓流式計算 ssc.start() ssc.awaitTermination() } }
6、sparkStreaming整合kafka
6.1、KafkaUtils.creataStream
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-i9kmH0VZ-1572241319581)(sparkStreaming整合kafka–基於receiver接收器.png)]
它是基於receiver接收器去拉取數據,默認數據會丟失,開啓WAL日誌把接受到的數據同步寫入到hdfs上保證數據源的安全性,後期某些rdd的部分分區數據丟失之後,可以通過血統+原始數據重新計算恢復得到。
可以保證數據不丟失,但是它保證不了數據被處理只被處理一次,會出現數據的重複處理。
-
添加依賴
<dependency> <groupId>org.apache.spark</groupId> <artifactId>spark-streaming-kafka-0-8_2.11</artifactId> <version>2.1.3</version> </dependency>
-
代碼開發
package cn.itcast.kafka import org.apache.spark.streaming.dstream.{DStream, ReceiverInputDStream} import org.apache.spark.{SparkConf, SparkContext} import org.apache.spark.streaming.{Seconds, StreamingContext} import org.apache.spark.streaming.kafka.KafkaUtils import scala.collection.immutable //todo:sparkStreaming整合kafka-----------kafka高級api(消息的偏移量保存zk,基於receiver接受器接受數據) object SparkStreamingKafkaReceiver { def main(args: Array[String]): Unit = { //1、創建SparkConf val sparkConf: SparkConf = new SparkConf() .setAppName("SparkStreamingKafkaReceiver") .setMaster("local[4]") //開啓WAL日誌,把接受到的數據寫入到HDFS上,保證數據源端的安全性 .set("spark.streaming.receiver.writeAheadLog.enable","true") //2、創建SparkContext val sc = new SparkContext(sparkConf) sc.setLogLevel("warn") //3、創建StreamingContext val ssc = new StreamingContext(sc,Seconds(5)) //設置一個checkpoint目錄,用於存儲接受到的數據,保證數據的安全 ssc.checkpoint("./spark-recever") //4、指定zk地址 val zkQuorum="node1:2181,node2:2181,node3:2181" //5、消費者組id val groupId="spark-receiver" //6、指定topic信息 key:表示topic的名稱,value:表示一個recevier接收器需要使用1個線程去拉取數據 val topics=Map("heima" ->1) /** ssc: StreamingContext, zkQuorum: String, groupId: String, topics: Map[String, Int], storageLevel: StorageLevel = StorageLevel.MEMORY_AND_DISK_SER_2 */ //7、通過receiver接收器接受kafka的topic數據 //(String, String)------> 第一個String表示消息的key, 第二個String就是消息內容 //默認使用了一個receiver接收器去接受數據,數據接受效率比較慢,後期可以使用多個receiver接收器接受數據 //開啓3個receiver接受數據,提升數據接收的效率 val totalReceiverSeq: immutable.IndexedSeq[ReceiverInputDStream[(String, String)]] = (1 to 3).map(x => { val kafkaStream: ReceiverInputDStream[(String, String)] = KafkaUtils.createStream(ssc, zkQuorum, groupId, topics) kafkaStream }) //通過調用union方法把所有recevier接收器得到的每一個Dstream進行合併生成一個Dstream val totalDstream: DStream[(String, String)] = ssc.union(totalReceiverSeq) //8、獲取topic的數據 val data: DStream[String] = totalDstream.map(x=>x._2) //9、切分每一行獲取所有的單詞 val words: DStream[String] = data.flatMap(_.split(" ")) //10、每個單詞計爲1 val wordAndOne: DStream[(String, Int)] = words.map((_,1)) //11、相同單詞出現的1累加 val result: DStream[(String, Int)] = wordAndOne.reduceByKey(_+_) //12、打印 result.print() //13 開啓流式計算 ssc.start() ssc.awaitTermination() } }
6.2 KafkaUtils.createDirectStream(優先考慮)
-
可以實現數據被處理且只被處理一次
就是通過數據處理和更新偏移量這2個操作在同一事務。 偏移量的保存可以保存在任何外部存儲介質中,比如 文件、mysql、zk、redis
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-6slDbTq5-1572241319583)(1.png)]
-
1、代碼開發
package cn.itcast.kafka import kafka.serializer.StringDecoder import org.apache.spark.streaming.dstream.{DStream, InputDStream} import org.apache.spark.{SparkConf, SparkContext} import org.apache.spark.streaming.{Seconds, StreamingContext} import org.apache.spark.streaming.kafka.KafkaUtils //todo:sparkStreaming整合kafka-------使用低級api(消息的偏移量不在由zk保存,客戶端自己去維護) object SparkStreamingDirectKafka { def main(args: Array[String]): Unit = { //1、創建SparkConf val sparkConf: SparkConf = new SparkConf() .setAppName("SparkStreamingDirectKafka") .setMaster("local[4]") //2、創建SparkContext val sc = new SparkContext(sparkConf) sc.setLogLevel("warn") //3、創建StreamingContext val ssc = new StreamingContext(sc,Seconds(5)) //設置checkpoint目錄------> 保存消息消費的偏移量的 ssc.checkpoint("./spark-direct") //4、指定kafka集羣參數 val kafkaParams=Map("bootstrap.servers"->"node1:9092,node2:9092,node3:9092","group.id" ->"spark-direct") //5、指定消費的topic名稱 val topics=Set("heima") val dstream: InputDStream[(String, String)] = KafkaUtils.createDirectStream[String,String,StringDecoder,StringDecoder](ssc,kafkaParams,topics) //6、獲取topic的內容 val data: DStream[String] = dstream.map(x=>x._2) //7、切分每一行獲取所有的單詞 val words: DStream[String] = data.flatMap(_.split(" ")) //8、每個單詞計爲1 val wordAndOne: DStream[(String, Int)] = words.map((_,1)) //9、相同單詞出現的1累加 val result: DStream[(String, Int)] = wordAndOne.reduceByKey(_+_) //10、打印 result.print() //11 開啓流式計算 ssc.start() ssc.awaitTermination() } }
-
2、程序邏輯梳理
這個時候並沒有使用receiver接收器去接受數據,也沒有把接受到的數據寫入到HDFS上,相對於第一套api代碼開發起來比較簡潔。 要想保證數據不丟失和數據被處理且只被處理一次: (1)數據處理 (2)保存偏移量 如果可以確保這2個操作在同一事務中。
-
3、打成jar包提交到集羣中運行
在實際開發中,先進行本地測試,本地測試只是看一下程序的處理邏輯對不對,如果有問題就及時修改,如果沒有問題就把該成打成jar包提交到集羣去運行。 spark-submit --master spark://node1:7077 --class cn.itcast.kafka.SparkStreamingDirectKafka --executor-memory 1g --total-executor-cores 8 original-spark_class15-1.0-SNAPSHOT.jar --executor-memory 1g --total-executor-cores 8 這個時候就需要給當前這個任務準備資源,這個資源到底給多少合理? 資源給多了就浪費,給少了跑的慢.. 我也不知道到底給定多少資源是合理的,但是我們可以找到一個比較合理的狀態。 比如說我們在代碼中的設置邏輯:每隔5s就處理上一個5s的數據。 由於不知道數據量,可能會出現一個批次的數據的處理時間是1分鐘。 第一個5s的批次數據: -------------------------------------> 1分鐘 第二個5s的批次數據: -------------------------------------> 1分鐘 第三個5s的批次數據: -------------------------------------> 1分鐘 第四個5s的批次數據: -------------------------------------> 1分鐘 ....... 上面這種情況下就會出現大量的job在等待,也就出現了數據的積壓,延遲是非常高。 最理想的狀態:就是在5s之內就把5s的數據處理完成。 這個時候可以訪問spark的web管理界面: master主機名;8080 去觀察每一個批次的數據處理時間 在給定資源的時候,可以進行幾組資源參數進行測試: (1)--executor-memory 5g --total-executor-cores 10 (2)--executor-memory 5g --total-executor-cores 20 (3)--executor-memory 6g --total-executor-cores 20 (4)--executor-memory 6g --total-executor-cores 30 最後找到一個最理想的參數,然後提交到集羣去運行。