第3課:通過案例對SparkStreaming 透徹理解三板斧之三:解密SparkStreaming運行機制和架構進階

本期內容:

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



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章