第5課:基於案例一節課貫通Spark Streaming流計算框架的運行源碼

通過以下案例切入對Spark Streaming流計算框架的運行源碼的解讀:


package com.dt.spark.cores
import org.apache.spark.SparkConf
import org.apache.spark.sql.Row
import org.apache.spark.sql.hive.HiveContext
import org.apache.spark.sql.types.{IntegerType, StringType, StructField, StructType}
import org.apache.spark.streaming.{Seconds, StreamingContext}
/**
  * 使用Spark Streaming+Spark SQL來在線動態計算電商中不同類別中最熱門的商品排名,例如手機這個類別下面最熱門的三種手機、電視這個類別
  * 下最熱門的三種電視,該實例在實際生產環境下具有非常重大的意義;
  * @author DT大數據夢工廠
  * 新浪微博:http://weibo.com/ilovepains/
  *  實現技術:Spark Streaming+Spark SQL,之所以Spark Streaming能夠使用ML、sql、graphx等功能是因爲有foreachRDD和Transform
  *  等接口,這些接口中其實是基於RDD進行操作,所以以RDD爲基石,就可以直接使用Spark其它所有的功能,就像直接調用API一樣簡單。
  *  假設說這裏的數據的格式:user item category,例如Rocky Samsung Android
  */
object OnlineTheTop3ItemForeachCategory2DB {
  def main(args: Array[String]) {
    val conf = new SparkConf();
    conf.setAppName("OnlineTheTop3ItemForeachCategory2DB");
    conf.setMaster("local[6]");
    /**
      * 此處設置Batch Interval是在Spark Streaming中生成基本job的時間單位,窗口和滑動時間間隔
      * 一定是改Batch Interval的整數倍
      * 設置batchDuration時間間隔來控制Job生成的頻率並且創建Spark Streaming執行的入口
      */
    val ssc = new StreamingContext(conf, Seconds(5));
    ssc.checkpoint("/root/Documents/SparkApps/")
    val userClickLogsDStream = ssc.socketTextStream("RockyLinux",9999)
    val formattedUserClickLogsDStream = userClickLogsDStream.map(clickLog =>
      (clickLog.split(" ")(2) + "_" + clickLog.split(" ")(1),1))
    val categorydUserClickLogsDStream = formattedUserClickLogsDStream.reduceByKeyAndWindow(_+_,_-_,Seconds(60),Seconds(20))
    categorydUserClickLogsDStream.foreachRDD{ rdd =>
      if(rdd.isEmpty()) {
        print("No rdd!!!")
      }else {
        val categoryItemRow = rdd.map(reducedItem => {
          val category = reducedItem._1.split("_")(0)
          val item = reducedItem._1.split("_")(1)
          val click_count = reducedItem._1.split("_")(2)
          Row(category, item, click_count)
        })
        val structType = StructType(Array(
          StructField("category", StringType, true),
          StructField("item", StringType, true),
          StructField("click_count", IntegerType, true)
        ))
        val hiveContext = new HiveContext(rdd.context)
        val categoryItemDF = hiveContext.createDataFrame(categoryItemRow, structType)
        categoryItemDF.registerTempTable("categoryItemTable")
        val reseltDataFram = hiveContext.sql("SELECT category,item,click_count FROM (SELECT category,item,click_count,row_number()" +
          " OVER (PARTITION BY category ORDER BY click_count DESC) rank FROM categoryItemTable) subquery " +
          " WHERE rank <= 3")
        val resultRowRDD = reseltDataFrame.rdd
        resultRowRDD.foreachPartition { partitionOfRecords => {
          if (partitionOfRecords.isEmpty) {null
            print("This RDD is not null but Partition is null!")
          } else {
            val connnection = ConnectionPool.getConnnection()
            partitionOfRecords.foreach(record => {
              val sql = "insert into categoryTop3(category,item,client_count) values('" + record.getAs("category") + "','"
              +record.getAs("item") + "'," + record.getAs("click_count") + ")"
              val stmt = connnection.createStatement()
              stmt.executeUpdate(sql)
            })
            ConnectionPool.returnConnection(connnection)
          }
         }
        }
       }
      }
    /**
      * 在StreamingContext調用start方法的內部其實是會啓動JobScheduler的start方法,進行消息循環,在JobScheduler
      * 的start內部會構造JobGenerator和ReceiverTracher,並且調用JobGenerator和ReceiverTacker的start方法:
      * 1.JobGenetor啓動後會不斷的根據batchDuratione生成一個個的Job
      * 2.ReceiverTracher啓動後首先在Spark Cluster中啓動Receiver(其實是在Executor中先啓動Receiver Supervisor)
      *   數據後會通過ReceiverSupervisor存儲到Executor並且把數據的Metadata信息發送給Driver中的ReceiverTracker,
      *   在ReceiverTracker內部會通過ReceiverBlockTracker來管理接受到的元數據信息
      * 每個BatchInterval會產生一個具體的Job,其實這裏的Job不是Spark Core中所指的Job,它只是基於DStreamGraph而生成的Receiver
      * 的DAG而已,從Java角度講,相當於Runnable接口實例,此時要想運行Job需要提交給JobScheduler,在Jobscheduler中
      * 通過線程單獨的線程來提交Job到集羣運行(其實是在線程中基於RDD的Action觸發正在的作業的運行),爲什麼使用線程池呢?
      * 1.作業不斷生成,所以爲了提升效率,我們需要線程池:這和在Executor中通過線程池執行Tack有異曲同工之妙;
      * 2.有可能設置了Job的FAIR公平調度的方式,這個時候也需要多線程的支持
      */
    ssc.start();
    ssc.awaitTermination();
  }
}


查看以上代碼運行的WebUI

wKiom1cu2m7BCjBGAAFQI9a6VIc880.png


wKiom1cu2njTHDrVAAG56cFDMBA870.png

我們過濾他的源碼

wKioL1cu21zQzw44AAJC-DWg-RM366.png

總體流程概述

wKiom1cu2ovwLOELAAIKn2kaqAk672.png


wKioL1cu23Gi4UG5AAIrU0XWen8959.png


wKiom1cu2p7iZnUhAAGelA29zGk244.png


wKiom1cu2qWDzWCNAAGpUrRwiNc855.png


wKioL1cu24mSCbttAAHwXF10sjU063.png


wKioL1cu25Cxu_TOAAH9yv6v9zk368.png

這就是他整個繼承結構

wKiom1cu2r_CsLokAAKecbM5Cjo448.png


wKiom1cu2sniOuNLAAJnX3zrAhQ429.png


wKioL1cu262REguHAAIUV7HOeLM559.png


wKioL1cu27SSeDQgAAGJVBvi2Ak608.png


wKiom1cu2uORKACaAAIPQkPlnt0830.png


wKioL1cu28SRMDy0AAJQrlntI6A445.png


wKioL1cu282DqI7eAAI1t5NiKCQ797.png


wKiom1cu2v2gp1oGAAI46iqusX0829.png


wKioL1cu29yju4n0AAHBmXl8uLA006.png


wKiom1cu2wjBhzKrAAGLkOEJk8A308.png


wKiom1cu2w7jSyhoAAGQh01oOUU698.png


wKiom1cu2xbhB8ORAAH6TywKeSU953.png


wKiom1cu2x7R0vWpAAIGAIHNeLY341.png


wKioL1cu3ATRNqrQAAHRHPL_FW4475.png


wKioL1cu3A-RS9ldAAHLxS9G5mc388.png


wKiom1cu2z7AonHiAAIazPY8Ej4750.png


wKioL1cu3CXird8bAAIFCZtrep4438.png


wKioL1cu3DCiJ7_mAAIQMS7lnxM786.png


wKioL1cu3DvityRjAAJVwUxFQjY211.png


wKioL1cu3EKyJYwmAAI-NZhlzFA456.png

備註:

這是我的Spark版本定製班學習筆記

更多私密內容,請關注微信公衆號:DT_Spark

如果您對大數據Spark感興趣,可以免費聽由王家林老師每天晚上20:00開設的Spark永久免費公開課,地址YY房間號:68917580


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