利用廣播變量來進行數據的傳輸

package report

import config.ConfigHelper
import org.apache.spark.broadcast.Broadcast
import org.apache.spark.rdd.RDD
import org.apache.spark.sql.SparkSession
import scalikejdbc.{DB, SQL}
import scalikejdbc.config.DBs
import utils.MakeATPKpi

//利用廣播變量來進行數據的傳輸
object TrainTimeBroadcastAnalysis {
  def main(args: Array[String]): Unit = {
    //session
    val session=SparkSession
      .builder()
      //設置進程的名稱
      .appName(this.getClass.getName)
      //設置是否是在本地運行
      //*代表幾個線程
      .master("local[*]")
      //序列化
      .config("spark.serializer",ConfigHelper.serializer)
      .getOrCreate()
    //導入隱式轉換
    import session.implicits._
    //讀取原始日誌
    val frame = session.read.parquet(args(0))
    //讀取列車出場時間字典表
    val trainTimeSource = session.sparkContext.textFile(args(1))
    //讀取以後對數據以"|"爲標準進行切分
    //spilt中的regax屬性裏面的"\\"是轉譯符號,除非後面的符號是","或者";",不然類似於"|"這樣的前面都要加上"\\"
    //limit
    //什麼都不加:300T//////      剪切完:300T
    //加-1:300T//////      剪切完:300T “” “” “” “” “” “”
    val rddSpilt: RDD[Array[String]] = trainTimeSource.map(_.split("\\|",-1))
    //清洗數據
    val rddFilted: RDD[Array[String]] = rddSpilt.filter(_.length>=2)
    //搞成元組rdd
    val rddTuple: RDD[(String, String)] = rddFilted.map(arr=>(arr(0),arr(1)))
    //先收集再轉換成map
//    val rddArr: Array[(String, String)] = rddTuple.collect()
//    val rddMap: Map[String, String] = rddArr.toMap
    //這個比上面的兩行更高效
    val rddMap: collection.Map[String, String] = rddTuple.collectAsMap()
    //創建廣播變量
    val trainTimeBro: Broadcast[collection.Map[String, String]] = session.sparkContext.broadcast(rddMap)
    //處理數據
    val rddResult: RDD[(String, List[Int])] = frame.map(row => {
      //獲取車號
      val trainId = row.getAs[String]("MPacketHead_TrainID")
      //獲取列車的出廠map
      val trainMap: collection.Map[String, String] = trainTimeBro.value
      //獲取列車出廠的時間
      //第一個參數是憑藉trainId去找trainTime
      //第二個參數是找不到trainTime就返回一個默認值
      val trainTime = trainMap.getOrElse(trainId, ",,,,")
      //獲取kpi
      //使用MakeATPKpi樣例類中的makeKpi方法,參數爲row
      //這條語句在map中,map可以理解爲有返回值的循環,因此參數爲row,表示每一行都要獲取數據
      val list: List[Int] = MakeATPKpi.makeKpi(row)
      //以列車的出廠時間爲key,list爲value,返回到map
      (trainTime, list)
    })
      //轉換成rdd,因爲只有rdd中才有reduceByKey方法
      .rdd
      //進行數據聚合

      //zip函數是把兩個list聚合
      //開發中,需要聚合的文段
      //舉個例子:
      //RDD中有如下元素
      //kv
      //((a,b),List(7,8,9))
      //((a,b),List(1,2,3))
      //((a,b),List(4,5,6))
      //((c,d),List(4,5,6))
      //。。。。。。
      //reducebykey後要對v進行操作
      //原理將v中的list前後zip再map每個元素進行逐個元組元素(a,b)的累加。
      //a代表v的前一個元素,b代表v的後一個元素
      //如val a=List(7,8,9)
      //val b=List  (1,2,3)
      //val k=  a zip  b  =((7,1),(8,2)(9,3))
      //k map (tp=>tp._1+tp._2)  ----- > (8,10,12)
      //如此循環 結果:
      //新RDD中的元素:
      //((a,b),List(12,15,18))
      //((c,d),List(4,5,6))
      //。。。。。。
      .reduceByKey {
      (list1, list2) => list1 zip list2 map (tp => tp._1 + tp._2)
    }
    //寫入mysql中,用scalikejdbc寫
    DBs.setup()
    //在driver端創建一個mysql鏈接,發送到從節點端
    //一個鏈接被n個從節點公用,排隊,十分影響效率
    //在excutor端中每一條數據創建一個鏈接,每次都獲取開線程,關線程,浪費時間
    //每個excutor用一個線程
    rddResult.foreachPartition(partition=>{
      DB.localTx{implicit session=>
        //遍歷
        partition.foreach(tp=>{
          SQL("insert into TrainTimeBroadcastAnalysis values (?,?,?,?,?,?,?,?,?,?,?)")
            //把rddResult中的幾個值都加入到insert語句中
            .bind(tp._1,tp._2(0),tp._2(1),tp._2(2),tp._2(3),tp._2(4),tp._2(5),tp._2(6),tp._2(7),tp._2(8),tp._2(9))
            .update()
            .apply()
        })
      }
    })
    //釋放資源
    session.stop()
  }

}

樣例類
MakeATPKpi.scala

package utils

import org.apache.commons.lang.StringUtils
import org.apache.spark.sql.Row

object MakeATPKpi {
  def makeKpi(row:Row)={
    //獲取atperror
    val atpError = row.getAs[String]("MATPBaseInfo_AtpError")
    //判斷指標
    //如果atpError不爲空,前面爲1,如果atpError爲空,那麼前面爲0
    //先判斷atpError不爲空,如果爲空,則執行下面的else語句,全部輸出爲0
    val listAtpError: List[Int] = if (StringUtils.isNotEmpty(atpError)) {
      val listError: List[Int] =
        if (atpError.equals("車載主機")) {
          List[Int](1, 0, 0, 0, 0, 0, 0, 0)
        } else if (atpError.equals("無線傳輸單元")) {
          List[Int](0, 1, 0, 0, 0, 0, 0, 0)
        } else if (atpError.equals("應答器信息接收單元")) {
          List[Int](0, 0, 1, 0, 0, 0, 0, 0)
        } else if (atpError.equals("軌道電路信息讀取器")) {
          List[Int](0, 0, 0, 1, 0, 0, 0, 0)
        } else if (atpError.equals("測速測距單元")) {
          List[Int](0, 0, 0, 0, 1, 0, 0, 0)
        } else if (atpError.equals("人機交互接口單元")) {
          List[Int](0, 0, 0, 0, 0, 1, 0, 0)
        } else if (atpError.equals("列車接口單元")) {
          List[Int](0, 0, 0, 0, 0, 0, 1, 0)
        } else if (atpError.equals("司法記錄單元")) {
          List[Int](0, 0, 0, 0, 0, 0, 0, 1)
        } else {
          //這是爲了防止獲取的atpError的值都不符合上述判斷條件的情況
          List[Int](0, 0, 0, 0, 0, 0, 0, 0)
        }
      //兩個list的拼接要用++
      List[Int](1) ++ listError
    } else {
      //如果atpError爲空的情況,說明這行的這個屬性沒有值,那麼全取爲0即可
      List[Int](0, 0, 0, 0, 0, 0, 0, 0, 0)
    }
    //創建一個容器用來存放標籤
    //兩個list的拼接要用++
    //這個是爲了在整合以後統計總共多少條數據用,相當於數量
    val list: List[Int] = List[Int](1) ++ listAtpError
    list
  }

}

相關配置文件
application.conf

#配置文件
#配置壓縮格式
parquet.code="snappy"
#配置序列化方式
spark.serializer="org.apache.spark.serializer.KryoSerializer"
#配置jdbc鏈接
jdbc.url="jdbc:mysql://localhost:3306/test?characterEncoding=UTF-8"
jdbc.driver="com.mysql.jdbc.Driver"
jdbc.user="root"
jdbc.password="000000"
#配置scalikejdbc鏈接
db.default.url="jdbc:mysql://localhost:3306/test?characterEncoding=UTF-8"
db.default.driver="com.mysql.jdbc.Driver"
db.default.user="root"
db.default.password="000000"

ConfigHelper.scala

package config

import com.typesafe.config.{Config, ConfigFactory}

object ConfigHelper {
  //加載配置文件
  private lazy val load: Config = ConfigFactory.load()
  //加載壓縮格式
  val parquetCode: String = load.getString("parquet.code")
  //加載序列化方式
  val serializer: String = load.getString("spark.serializer")
  //加載jdbc
  val url: String = load.getString("jdbc.url")
  val driver: String = load.getString("jdbc.driver")
  val user: String = load.getString("jdbc.user")
  val password: String = load.getString("jdbc.password")
  //加載scalikejdbc
  val url2: String = load.getString("db.default.url")
  val driver2: String = load.getString("db.default.driver")
  val user2: String = load.getString("db.default.user")
  val password2: String = load.getString("db.default.password")

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