Spark-Streaming及其工作原理

1.Spark-Streaming及其工作原理
Spark Streaming是Spark Core API的一種擴展,它可以用於進行大規模、高吞吐量、容錯的實時數據流的處理。它支持從很多種數據源中讀取數據,比如Kafka、Flume、Twitter、ZeroMQ、Kinesis或者是TCP Socket。並且能夠使用類似高階函數的複雜算法來進行數據處理,比如map、reduce、join和window。處理後的數據可以被保存到文件系統、數據庫、Dashboard等存儲中。

Spark Streaming基本工作原理:Spark Streaming內部的基本工作原理如下:接收實時輸入數據流,然後將數據拆分成多個batch,比如每收集1秒的數據封裝爲一個batch,然後將每個batch交給Spark的計算引擎進行處理,最後會生產出一個結果數據流,其中的數據,也是由一個一個的batch所組成的。

2.DStream介紹
Spark Streaming提供了一種高級的抽象,叫做DStream,英文全稱爲Discretized Stream,中文翻譯爲“離散流”,它代表了一個持續不斷的數據流。DStream可以通過輸入數據源來創建,比如Kafka、Flume和Kinesis;也可以通過對其他DStream應用高階函數來創建,比如map、reduce、join、window。
DStream的內部,其實一系列持續不斷產生的RDD。RDD是Spark Core的核心抽象,即,不可變的,分佈式的數據集。DStream中的每個RDD都包含了一個時間段內的數據。
對DStream應用的算子,比如map,其實在底層會被翻譯爲對DStream中每個RDD的操作。比如對一個DStream執行一個map操作,會產生一個新的DStream。但是,在底層,其實其原理爲,對輸入DStream中每個時間段的RDD,都應用一遍map操作,然後生成的新的RDD,即作爲新的DStream中的那個時間段的一個RDD。底層的RDD的transformation操作,其實,還是由Spark Core的計算引擎來實現的。Spark Streaming對Spark Core進行了一層封裝,隱藏了細節,然後對開發人員提供了方便易用的高層次的API。

3.Spark Streaming與Storm差別
3.1 Spark Streaming與Storm的對比
Storm:實時計算模型(純實時,來一條數據,處理一條數據);實時計算延遲度(毫秒級);吞吐量(低);事務機制(支持完善);健壯性 / 容錯性(ZooKeeper,Acker,非常強);動態調整並行度(支持)
Spark Streaming:實時計算模型(準實時,對一個時間段內的數據收集起來,作爲一個RDD,再處理);實時計算延遲度(秒級);吞吐量(高);事務機制(支持,但不夠完善);健壯性 / 容錯性(Checkpoint,WAL,一般);動態調整並行度(不支持)

3.2 Spark Streaming與Storm的優劣分析
事實上,Spark Streaming絕對談不上比Storm優秀。這兩個框架在實時計算領域中,都很優秀,只是擅長的細分場景並不相同。
Spark Streaming僅僅在吞吐量上比Storm要優秀,而吞吐量這一點,也是歷來挺Spark Streaming,貶Storm的人着重強調的。但是問題是,是不是在所有的實時計算場景下,都那麼注重吞吐量?不盡然。因此,通過吞吐量說Spark Streaming強於Storm,不靠譜。
事實上,Storm在實時延遲度上,比Spark Streaming就好多了,前者是純實時,後者是準實時。而且,Storm的事務機制、健壯性 / 容錯性、動態調整並行度等特性,都要比Spark Streaming更加優秀。
Spark Streaming,有一點是Storm絕對比不上的,就是:它位於Spark生態技術棧中,因此Spark Streaming可以和Spark Core、Spark SQL無縫整合,也就意味着,我們可以對實時處理出來的中間數據,立即在程序中無縫進行延遲批處理、交互式查詢等操作。這個特點大大增強了Spark Streaming的優勢和功能。

3.3 Spark Streaming與Storm的應用場景
對於Storm來說:
(1)、建議在那種需要純實時,不能忍受1秒以上延遲的場景下使用,比如實時金融系統,要求純實時進行金融交易和分析
(2)、此外,如果對於實時計算的功能中,要求可靠的事務機制和可靠性機制,即數據的處理完全精準,一條也不能多,一條也不能少,也可以考慮使用Storm
(3)、如果還需要針對高峯低峯時間段,動態調整實時計算程序的並行度,以最大限度利用集羣資源(通常是在小型公司,集羣資源緊張的情況),也可以考慮用Storm
(4)、如果一個大數據應用系統,它就是純粹的實時計算,不需要在中間執行SQL交互式查詢、複雜的transformation算子等,那麼用Storm是比較好的選擇

對於Spark Streaming來說:
(1)、如果對上述適用於Storm的三點,一條都不滿足的實時場景,即,不要求純實時,不要求強大可靠的事務機制,不要求動態調整並行度,那麼可以考慮使用Spark Streaming
(2)、考慮使用Spark Streaming最主要的一個因素,應該是針對整個項目進行宏觀的考慮,即,如果一個項目除了實時計算之外,還包括了離線批處理、交互式查詢等業務功能,而且實時計算中,可能還會牽扯到高延遲批處理、交互式查詢等功能,那麼就應該首選Spark生態,用Spark Core開發離線批處理,用Spark SQL開發交互式查詢,用Spark Streaming開發實時計算,三者可以無縫整合,給系統提供非常高的可擴展性。

4.wordcount實時統計案例
object WordCountDemo {
		  def main(args: Array[String]): Unit = {
		    LoggerLevels.setStreamingLogLevels()
		    //local[2]這裏必須是2個或2個以上的線程,一個負責接收數據,一個負責將接收的數據下發到worker上執行
		    val config = new SparkConf().setAppName("WordCountDemo").setMaster("local[2]")

		    //這種方式創建streamingContxt也行
		    // val ssc = new StreamingContext(config, Seconds(2))
		    //這種方式創建streamingContxt也行
		    val sc = new SparkContext(config)
		    //Seconds兩秒產生一個RDD
		    val ssc = new StreamingContext(sc, Seconds(2))
		    //使用updateStateByKey必須設置checkpoint,以防數據丟失後可以從這個目錄裏面找到
		    ssc.checkpoint("hdfs://hadoop01:8020/checkPoint")
		    //創建一個輸入的Dstream,你的在hadoop01,節點上啓動一個8008 端口(命令:nc -l 8008   ),如果沒有nc這個命令
		    //可以使用這個命令安裝  : yum install -y nc  用root用戶來安裝,前提你虛擬機已經聯網,或則可以去網上下載nc的包自己安裝
		    //安裝完查看nc幫助 nc -help ,如果有說明已經安裝上了
		    // 一個輸入數據流Dstream,綁定一個Receiver,一個Receiver會佔用一個core
		    //佔用的這個core不會被釋放,一直要等待程序的結束,這個被佔用的core纔會被釋放,所以在做任務提交時,分配的executor-cores一定要
		    //大於你在程序中創建的dsteam的個數,也就是recevicer的個數。
		    val socketDS = ssc.socketTextStream("hadoop01", 8008)

		    //這個是每次只能計算獲取那一批的數據,不能把之前獲取的數據也進行累加
		    // val wordCountDS = socketDS.flatMap(_.split(" ")).map((_, 1)).reduceByKey(_ + _)
		    //將獲取到數據進行累加
		    val wordCountDS = socketDS.flatMap(_.split(" ")).map((_, 1))
		      .updateStateByKey((iter: Iterator[(String, Seq[Int], Option[Int])]) => {
		        //(String, Seq[Int], Option[Int])
		        // String 代表的是key,
		        // Seq[Int],代表以前出現的次數Seq[1,2,4],
		        // Option[Int]代表是本次出現的次數,本次可能出現,也可能沒有出現
		        //x._1是key,x._2.sum是上一次出現的次數,x._3.getOrElse(0)是這一次出現的次數
		        iter.map(x => (x._1, x._2.sum + x._3.getOrElse(0)))
		      }, new HashPartitioner(sc.defaultParallelism), true)
		    wordCountDS.print()

		    //調用StreamingContext的start()方法,來開始實時處理數據。
		    ssc.start()
		    //調用StreamingContext的awaitTermination()方法,來等待應用程序的終止
		    ssc.awaitTermination()
		  }
		}

5.StreamingContext詳解
5.1 有兩種創建StreamingContext的方式:
方法一:
val conf = new SparkConf().setAppName(appName).setMaster(master);
val ssc = new StreamingContext(conf, Seconds(1));
方法二:
StreamingContext,還可以使用已有的SparkContext來創建
val sc = new SparkContext(conf)
val ssc = new StreamingContext(sc, Seconds(1));
註釋:appName,是用來在Spark UI上顯示的應用名稱。master,是一個Spark、Mesos或者Yarn集羣的URL,或者是local[*]。

5.2 一個StreamingContext定義之後,必須做以下幾件事情:
(1)、通過創建輸入DStream來創建輸入數據源。
(2)、通過對DStream定義transformation和output算子操作,來定義實時計算邏輯。
(3)、調用StreamingContext的start()方法,來開始實時處理數據。
(4)、調用StreamingContext的awaitTermination()方法,來等待應用程序的終止。可以使用CTRL+C手動停止,或者就是讓它持續不斷的運行進行計算。
(5)、也可以通過調用StreamingContext的stop()方法,來停止應用程序。
5.3 需要注意的要點:
(1)、只要一個StreamingContext啓動之後,就不能再往其中添加任何計算邏輯了。比如執行start()方法之後,還給某個DStream執行一個算子。
(2)、一個StreamingContext停止之後,是肯定不能夠重啓的。調用stop()之後,不能再調用start()
(3)、一個JVM同時只能有一個StreamingContext啓動。在你的應用程序中,不能創建兩個StreamingContext。
(4)、調用stop()方法時,會同時停止內部的SparkContext,如果不希望如此,還希望後面繼續使用SparkContext創建其他類型的Context,比如SQLContext,那麼就用stop(false)。
(5)、一個SparkContext可以創建多個StreamingContext,只要上一個先用stop(false)停止,再創建下一個即可。

6.輸入DStream和Receiver詳解
輸入DStream代表了來自數據源的輸入數據流。在之前的wordcount例子中,lines就是一個輸入DStream(JavaReceiverInputDStream),代表了從netcat(nc)服務接收到的數據流。除了文件數據流之外,所有的輸入DStream都會綁定一個Receiver對象,該對象是一個關鍵的組件,用來從數據源接收數據,並將其存儲在Spark的內存中,以供後續處理。

Spark Streaming提供了兩種內置的數據源支持
(1)、基礎數據源:StreamingContext API中直接提供了對這些數據源的支持,比如文件、socket、Akka Actor等。
(2)、高級數據源:諸如Kafka、Flume、Kinesis、Twitter等數據源,通過第三方工具類提供支持。這些數據源的使用,需要引用其依賴。
(3)、自定義數據源:我們可以自己定義數據源,來決定如何接受和存儲數據。

要注意的是,如果你想要在實時計算應用中並行接收多條數據流,可以創建多個輸入DStream。這樣就會創建多個Receiver,從而並行地接收多個數據流。但是要注意的是,一個Spark Streaming Application的Executor,是一個長時間運行的任務,因此,它會獨佔分配給Spark Streaming Application的cpu core。從而只要Spark Streaming運行起來以後,這個節點上的cpu core,就沒法給其他應用使用了。

使用本地模式,運行程序時,絕對不能用local或者local[1],因爲那樣的話,只會給執行輸入DStream的executor分配一個線程。而Spark Streaming底層的原理是,至少要有兩條線程,一條線程用來分配給Receiver接收數據,一條線程用來處理接收到的數據。因此必須使用local[n],n>=2的模式。

如果不設置Master,也就是直接將Spark Streaming應用提交到集羣上運行,那麼首先,必須要求集羣節點上,有>1個cpu core,其次,給Spark Streaming的每個executor分配的core,必須>1,這樣,才能保證分配到executor上運行的輸入DStream,兩條線程並行,一條運行Receiver,接收數據;一條處理數據。否則的話,只會接收數據,不會處理數據。

發佈了45 篇原創文章 · 獲贊 23 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章