本期內容:
1 解密Spark Streaming Job架構和運行機制
2 解密Spark Streaming容錯架構和運行機制
一、首先從一個SparkStreaming例子
package com.dt.spark.sparkstreaming
import org.apache.spark.SparkConf
import org.apache.spark.streaming.{Seconds, StreamingContext}
/**
* 使用Scala開發集羣運行的Spark 在線黑名單過濾程序
*
* @author DT大數據夢工廠
* 新浪微博:http://weibo.com/ilovepains/
*
* 背景描述:在廣告點擊計費系統中,我們在線過濾掉黑名單的點擊,進而保護廣告商的利益,只進行有效的廣告點擊計費
* 或者在防刷評分(或者流量)系統,過濾掉無效的投票或者評分或者流量;
* 實現技術:使用transform Api直接基於RDD編程,進行join操作
*
*/
object OnlineForeachRDD2DB {
def main(args: Array[String]){
/**
* 第1步:創建Spark的配置對象SparkConf,設置Spark程序的運行時的配置信息,
* 例如說通過setMaster來設置程序要鏈接的Spark集羣的Master的URL,如果設置
* 爲local,則代表Spark程序在本地運行,特別適合於機器配置條件非常差(例如
* 只有1G的內存)的初學者 *
*/
val conf = new SparkConf() //創建SparkConf對象
conf.setAppName("OnlineForeachRDD") //設置應用程序的名稱,在程序運行的監控界面可以看到名稱
// conf.setMaster("spark://Master:7077") //此時,程序在Spark集羣
conf.setMaster("local[6]")
//設置batchDuration時間間隔來控制Job生成的頻率並且創建Spark Streaming執行的入口
val ssc = new StreamingContext(conf, Seconds(5))
val lines = ssc.socketTextStream("Master", 9999)
val words = lines.flatMap(_.split(" "))
val wordCounts = words.map(x => (x, 1)).reduceByKey(_ + _)
wordCounts.foreachRDD { rdd =>
rdd.foreachPartition { partitionOfRecords => {
// ConnectionPool is a static, lazily initialized pool of connections
val connection = ConnectionPool.getConnection()
partitionOfRecords.foreach(record => {
val sql = "insert into streaming_itemcount(item,count) values('" + record._1 + "'," + record._2 + ")"
val stmt = connection.createStatement();
stmt.executeUpdate(sql);
})
ConnectionPool.returnConnection(connection) // return to the pool for future reuse
}
}
}
/**
* 在StreamingContext調用start方法的內部其實是會啓動JobScheduler的Start方法,進行消息循環,在JobScheduler
* 的start內部會構造JobGenerator和ReceiverTacker,並且調用JobGenerator和ReceiverTacker的start方法:
* 1,JobGenerator啓動後會不斷的根據batchDuration生成一個個的Job
* 2,ReceiverTracker啓動後首先在Spark Cluster中啓動Receiver(其實是在Executor中先啓動ReceiverSupervisor),在Receiver收到
* 數據後會通過ReceiverSupervisor存儲到Executor並且把數據的Metadata信息發送給Driver中的ReceiverTracker,在ReceiverTracker
* 內部會通過ReceivedBlockTracker來管理接受到的元數據信息
* 每個BatchInterval會產生一個具體的Job,其實這裏的Job不是Spark Core中所指的Job,它只是基於DStreamGraph而生成的RDD
* 的DAG而已,從Java角度講,相當於Runnable接口實例,此時要想運行Job需要提交給JobScheduler,在JobScheduler中通過線程池的方式找到一個
* 單獨的線程來提交Job到集羣運行(其實是在線程中基於RDD的Action觸發真正的作業的運行),爲什麼使用線程池呢?
* 1,作業不斷生成,所以爲了提升效率,我們需要線程池;這和在Executor中通過線程池執行Task有異曲同工之妙;
* 2,有可能設置了Job的FAIR公平調度的方式,這個時候也需要多線程的支持;
*
*/
ssc.start()
ssc.awaitTermination()
}
}
Executor級別容錯:
(a) 接收數據的安全性: (1) StorageLevel.MEMORY_AND_DISK_SER_2 (2) 通過WAL(Write Ahead Log),通常生產環境會和Kafka結合使用;
(b) 執行的安全性: JOB執行的安全性通過RDD容錯。
Driver級別容錯:DAG生成模板 DStreamGraph,Receiver Tracker,JobGenerator等 每個Job生成 執行Checkpoint